91 lines
4.0 KiB
TypeScript
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');
|
|
});
|
|
});
|