19096552b4
- RegisterView: email + password (strength-meter 0..4) + 2 click-wrap'а (оферта + ПДн). 3-й «маркетинг» из handoff НЕ реализован (расхождение #2 реестра v1.13 - handoff противоречит ТЗ §1.5/§4.1). - TwoFactorView: 6 input-cell с auto-focus вперёд при вводе цифры, Backspace назад при empty, paste 6 цифр заполняет все. - ForgotPasswordView: email + alert «5 попыток / 15 минут» по ТЗ §1.7. - RecoveryCodesView: 8 кодов в 2-column grid + Скачать .txt (Blob/URL.createObjectURL) + Копировать (navigator.clipboard) + warning о невозможности повторного просмотра. Router: 4 новых маршрута (/register, /2fa, /forgot, /recovery), все meta.layout='auth', lazy-imports. Vitest +14 тестов (всего 24/24 за 3.29s): - RegisterView 4 (вкл. assertion на отсутствие маркетингового click-wrap) - TwoFactorView 3, ForgotPasswordView 3, RecoveryCodesView 4 Stories +4 (Histoire 6/6 за 29.17s). Регресс: lint+type-check+format OK; vitest 24/24; vite build 5 lazy-chunks для views + Vuetify в отдельные chunks (app chunk 198KB→78KB); Pest 48/48 за 4.85s. CLAUDE.md v1.19→v1.20, реестр Открытых_вопросов v1.28→v1.29. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
45 lines
1.7 KiB
TypeScript
45 lines
1.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { mount } from '@vue/test-utils';
|
|
import { createVuetify } from 'vuetify';
|
|
import { createRouter, createMemoryHistory } from 'vue-router';
|
|
import TwoFactorView from '../../resources/js/views/auth/TwoFactorView.vue';
|
|
|
|
const mountTwoFactor = async () => {
|
|
const router = createRouter({
|
|
history: createMemoryHistory(),
|
|
routes: [
|
|
{ path: '/2fa', component: TwoFactorView },
|
|
{ path: '/recovery', component: { template: '<div>stub</div>' } },
|
|
],
|
|
});
|
|
await router.push('/2fa');
|
|
await router.isReady();
|
|
return mount(TwoFactorView, {
|
|
global: { plugins: [createVuetify(), router] },
|
|
});
|
|
};
|
|
|
|
describe('TwoFactorView.vue', () => {
|
|
it('монтируется и содержит заголовок', async () => {
|
|
const wrapper = await mountTwoFactor();
|
|
expect(wrapper.text()).toContain('Двухфакторная проверка');
|
|
});
|
|
|
|
it('содержит ровно 6 input-cell для кода', async () => {
|
|
const wrapper = await mountTwoFactor();
|
|
const cells = wrapper.findAll('.code-cell');
|
|
expect(cells).toHaveLength(6);
|
|
// Каждая cell — numeric inputmode + maxlength=1.
|
|
cells.forEach((cell) => {
|
|
expect(cell.attributes('inputmode')).toBe('numeric');
|
|
expect(cell.attributes('maxlength')).toBe('1');
|
|
});
|
|
});
|
|
|
|
it('содержит ссылку на резервный код', async () => {
|
|
const wrapper = await mountTwoFactor();
|
|
const links = wrapper.findAll('a').map((a) => a.text());
|
|
expect(links.some((t) => t.includes('резервный код'))).toBe(true);
|
|
});
|
|
});
|