Files
portal/app/tests/Frontend/LoginView.spec.ts
T

91 lines
4.0 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createVuetify } from 'vuetify';
import { createRouter, createMemoryHistory } from 'vue-router';
import LoginView from '../../resources/js/views/auth/LoginView.vue';
import { useAuthStore } from '../../resources/js/stores/auth';
// Smoke-тесты LoginView: монтируется внутри router-context, содержит
// все три ключевых элемента из v8_login.html секция #form-login —
// заголовок, кнопку «Войти», SSO Yandex 360.
const mountLoginView = async () => {
const pinia = createPinia();
setActivePinia(pinia);
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/login', name: 'login', component: LoginView },
{ path: '/register', name: 'register', component: { template: '<div>stub</div>' } },
{ path: '/forgot', name: 'forgot', component: { template: '<div>stub</div>' } },
],
});
await router.push('/login');
await router.isReady();
return mount(LoginView, {
global: {
plugins: [pinia, createVuetify(), router],
},
});
};
describe('LoginView.vue', () => {
it('монтируется без ошибок', async () => {
const wrapper = await mountLoginView();
expect(wrapper.exists()).toBe(true);
});
it('содержит заголовок «Вход в Лидерру»', async () => {
const wrapper = await mountLoginView();
expect(wrapper.text()).toContain('Вход в Лидерру');
});
it('содержит кнопку «Войти» и Yandex 360 SSO', async () => {
const wrapper = await mountLoginView();
expect(wrapper.text()).toContain('Войти');
expect(wrapper.text()).toContain('Yandex 360');
});
it('содержит поля email и пароль с правильным autocomplete', async () => {
const wrapper = await mountLoginView();
const emailInput = wrapper.find('input[type="email"]');
expect(emailInput.exists()).toBe(true);
expect(emailInput.attributes('autocomplete')).toBe('email');
const passwordInput = wrapper.find('input[type="password"]');
expect(passwordInput.exists()).toBe(true);
expect(passwordInput.attributes('autocomplete')).toBe('current-password');
});
it('содержит ссылки на регистрацию и сброс пароля', async () => {
const wrapper = await mountLoginView();
const links = wrapper.findAll('a').map((a) => a.text());
expect(links.some((t) => t.includes('Зарегистрируйтесь'))).toBe(true);
expect(links.some((t) => t.includes('Забыли пароль'))).toBe(true);
});
it('показывает lockout-alert когда auth.lockoutSeconds установлен (rate-limit 429)', async () => {
const wrapper = await mountLoginView();
// По умолчанию alert не отображается.
expect(wrapper.find('[data-testid="lockout-alert"]').exists()).toBe(false);
const auth = useAuthStore();
auth.lockoutSeconds = 600; // 10 минут
await wrapper.vm.$nextTick();
const alert = wrapper.find('[data-testid="lockout-alert"]');
expect(alert.exists()).toBe(true);
// 600 / 60 = 10 мин (округление вверх).
expect(alert.text()).toContain('10 мин');
expect(alert.text()).toContain('Слишком много попыток');
});
it('A1: SSO Yandex 360 — кнопка disabled до подключения Б-1', async () => {
const wrapper = await mountLoginView();
const ssoBtn = wrapper.findAll('button').find((b) => b.text().includes('Yandex 360'));
expect(ssoBtn).toBeDefined();
expect(ssoBtn!.classes()).toContain('v-btn--disabled');
});
});