/** * Sprint 17 — flow 09 onboarding wizard (4 шага): * 9.1 wizard рендерится на /onboarding-wizard и показывает 4 шага через progress-bar * 9.2 skip всех шагов → /dashboard + localStorage.fm.wizardCompleted=1 * 9.3 шаг 1: сохранение названия магазина обновляет org.name * 9.4 /help рендерит markdown topics + поиск * 9.5 /admin/diagnostic возвращает 7 проверок (sendTestEmail=false) * 9.6 POST /api/feedback с минимальным payload → ok * 9.7 /api/whats-new возвращает массив items */ import { expect, test } from '@playwright/test' import { OrgFactory } from '../factories/OrgFactory.js' import { request } from '../factories/api-client.js' import { attachSession } from '../lib/ui.js' test.describe('flow 09 — onboarding wizard + help + diagnostic + feedback + whats-new', () => { test('9.1 wizard рендерится с 4-шаговым progress-bar @smoke', async ({ page }) => { const b = await OrgFactory.for('wiz91').build() await attachSession(page, b.session, '/onboarding-wizard') await expect(page.getByText(/Шаг 1 из 4/)).toBeVisible() await expect(page.getByRole('heading', { name: /Магазин/i })).toBeVisible() }) test('9.2 skip всех 4 шагов → /dashboard + wizardCompleted', async ({ page }) => { const b = await OrgFactory.for('wiz92').build() await attachSession(page, b.session, '/onboarding-wizard') // Шаг 1: «Пропустить» (две кнопки — основная и нижняя; используем основную в footer'е модала). await page.getByRole('button', { name: 'Пропустить', exact: true }).first().click() // Шаг 2. await expect(page.getByText(/Шаг 2 из 4/)).toBeVisible() await page.getByRole('button', { name: 'Пропустить', exact: true }).first().click() // Шаг 3 — кнопка «Сделать позже». await expect(page.getByText(/Шаг 3 из 4/)).toBeVisible() await page.getByRole('button', { name: /Сделать позже/i }).click() // Шаг 4 — «Не нужно» завершает. await expect(page.getByText(/Шаг 4 из 4/)).toBeVisible() await page.getByRole('button', { name: /Не нужно/i }).click() await page.waitForURL(/\/dashboard/, { timeout: 10_000 }) const flag = await page.evaluate(() => localStorage.getItem('fm.wizardCompleted')) expect(flag).toBe('1') }) test('9.3 шаг 1: сохранение названия магазина обновляет org', async ({ page }) => { const b = await OrgFactory.for('wiz93').build() await attachSession(page, b.session, '/onboarding-wizard') const newName = `WizardOrg-${Date.now()}` await page.getByLabel(/Название магазина/).fill(newName) await page.getByRole('button', { name: /Дальше/i }).click() await expect(page.getByText(/Шаг 2 из 4/)).toBeVisible() // Проверяем что сохранилось. const settings = await request<{ name: string }>( '/api/organization/settings', { token: b.session.accessToken }, ) expect(settings.name).toBe(newName) }) test('9.4 /help рендерит markdown topics с поиском @smoke', async ({ page }) => { const b = await OrgFactory.for('wiz94').build() await attachSession(page, b.session, '/help') await expect(page.getByRole('heading', { name: /База знаний/i })).toBeVisible() // Должны быть основные топики. await expect(page.getByText(/Начало работы/i).first()).toBeVisible() await expect(page.getByText(/Каталог/i).first()).toBeVisible() // Поиск. await page.getByPlaceholder(/Поиск по темам/).fill('лояльность') await expect(page.getByText(/Программы скидок|Лояльность/i).first()).toBeVisible({ timeout: 3_000 }) }) test('9.5 /api/admin/diagnostic/run возвращает 7 проверок @smoke', async () => { const b = await OrgFactory.for('wiz95').build() const r = await request<{ overall: string; checks: Array<{ name: string; status: string }>; ranAt: string }>( '/api/admin/diagnostic/run?sendTestEmail=false', { token: b.session.accessToken }, ) expect(r.checks.length).toBeGreaterThanOrEqual(7) const names = r.checks.map(c => c.name) expect(names).toContain('Database') expect(names).toContain('SMTP') expect(names).toContain('Hangfire') expect(names).toContain('Disk') expect(names).toContain('Backup') // overall должен быть Ok / Warning / Fail / Skipped. expect(['Ok', 'Warning', 'Fail', 'Skipped']).toContain(r.overall) }) test('9.6 POST /api/feedback принимает минимальный payload', async () => { const b = await OrgFactory.for('wiz96').build() const r = await request<{ ok: boolean }>( '/api/feedback', { token: b.session.accessToken, body: { category: 1, message: 'Тестовое сообщение из regression test' }, }, ) expect(r.ok).toBe(true) }) test('9.7 /api/whats-new возвращает buildVersion + items', async () => { const b = await OrgFactory.for('wiz97').build() const r = await request<{ buildVersion: string; items: Array<{ date: string; title: string; type: string }> }>( '/api/whats-new', { token: b.session.accessToken }, ) expect(typeof r.buildVersion).toBe('string') expect(Array.isArray(r.items)).toBe(true) }) })