Мастер настройки проще: один вопрос на экран, подробности — под «Подробнее»

- «Время и сеть»: пояс компьютера пользователя предлагается одной кнопкой, пояс применяется сразу при
  выборе; сервер времени — только если не синхронизируется; «сделано установщиком» — строкой внизу.
- «Лицензия»: модули словами, а не кодами (quik-mnp → «QUIK: неторговые поручения…»); пояснения — только
  пока ключа нет; поле ключа крупное, одно.
- «Учёт DEPO»: главный вопрос «Как подготовить базу» первым, короткие подписи; кружки переключателей
  больше не сплющиваются длинным текстом.
- «QUIK»: одна кнопка «Сохранить и проверить»; база и шифрование — под «Дополнительно», без «true/disable»
  и TrustServerCertificate; автоматический цикл и справочник получателей в мастере свёрнуты.
- «Организация»: без логотипа (он в «Оформлении»), кнопки одного цвета. «Шлюз НРД», «Ключ», «Оповещения»:
  по одному абзацу, письма и устройство автообновления сертификатов — под раскрытием.
- Мастер и «Подготовка модулей» — без меню системы; подзаголовок не повторяет названия глав.
- Подсказка о восстановлении из копии ведёт на первую страницу (в мастере его больше нет).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-06 11:58:58 +03:00
parent 8905ebc6fb
commit 6a68dc927f
13 changed files with 234 additions and 133 deletions
+80
View File
@@ -377,3 +377,83 @@ func TestDepoSetupScript(t *testing.T) {
t.Fatalf("пароль: %q", p)
}
}
// Шаги мастера отрисовываются без ошибок шаблона: лицензия, учёт DEPO одним шагом, время и сеть, проверка.
func TestWizardStepsRender(t *testing.T) {
a, err := newAdmin()
if err != nil {
t.Fatal(err)
}
priv := testLicenseKey(t)
rc := testRC(t)
now := time.Now().UTC()
tok := signTestLicense(t, priv, &license.License{Schema: 1, ID: "lic-1", Tenant: "Тест", Product: "bj-server", Plan: license.PlanPro,
IssuedAt: now, ExpiresAt: now.Add(30 * 24 * time.Hour), ContractUntil: now.AddDate(1, 0, 0), Features: []string{"updates", "quik-mnp"}})
if err := rc.SaveLicense(LicenseSettings{Key: tok}); err != nil {
t.Fatal(err)
}
h := &setupHandlers{rc: rc, tpl: &adminTemplates{a: a}}
for step, want := range map[string]string{"1": "QUIK: неторговые поручения", "3": "wiz-depo", "5": "Сохранить и проверить", "12": "wiz-tz-suggest", "10": "Проверка"} {
w := httptest.NewRecorder()
h.renderWizard(w, httptest.NewRequest(http.MethodGet, "/admin/wizard?step="+step, nil))
body := w.Body.String()
if d := os.Getenv("BJ_DUMP_PAGES"); d != "" {
_ = os.WriteFile(filepath.Join(d, "wizard-"+step+".html"), []byte(body), 0o644)
}
if w.Code != http.StatusOK || !strings.Contains(body, want) {
i := strings.Index(body, "<main")
t.Fatalf("шаг %s: код %d, нет %q\n%s", step, w.Code, want, body[max(i, 0):min(len(body), max(i, 0)+1500)])
}
if step == "3" && (strings.Contains(body, "BJ_DEPO_DATA") || !strings.Contains(body, "depo_job")) {
t.Fatal("шаг DEPO: имя нашей базы в форме или нет выбора, где работает задание")
}
if strings.Contains(body, "wiz-restore") {
t.Fatal("восстановление из копии — только на первой странице, не в мастере")
}
}
}
// Снимки страниц первичной настройки для проверки глазами: BJ_DUMP_PAGES=<каталог> go test -run TestDumpOnboardingPages.
func TestDumpOnboardingPages(t *testing.T) {
dir := os.Getenv("BJ_DUMP_PAGES")
if dir == "" {
t.Skip("BJ_DUMP_PAGES не задан")
}
priv := testLicenseKey(t)
a, err := newAdmin()
if err != nil {
t.Fatal(err)
}
rc := testRC(t)
now := time.Now().UTC()
tok := signTestLicense(t, priv, &license.License{Schema: 1, ID: "lic-1", Tenant: "АО «Пример Инвест»", Product: "bj-server", Plan: license.PlanEnterprise,
IssuedAt: now, ExpiresAt: now.Add(365 * 24 * time.Hour), ContractUntil: now.AddDate(1, 0, 0), Features: []string{"updates", "quik-mnp", "web-cabinet", "npr", "quik-export"}})
if err := rc.SaveLicense(LicenseSettings{Key: tok}); err != nil {
t.Fatal(err)
}
h := &setupHandlers{rc: rc, tpl: &adminTemplates{a: a}}
for step := 1; step <= wizardSteps; step++ {
w := httptest.NewRecorder()
h.renderWizard(w, httptest.NewRequest(http.MethodGet, fmt.Sprintf("/admin/wizard?step=%d", step), nil))
_ = os.WriteFile(filepath.Join(dir, fmt.Sprintf("wizard-%02d.html", step)), w.Body.Bytes(), 0o644)
}
var b strings.Builder
d := PrepareData{page: page{Title: "Подготовка модулей", Active: "wizard"}, Tenant: "АО «Пример Инвест»", Plan: "enterprise", Until: "поддержка до 06.10.2027", Next: "/admin/wizard?step=12", Flash: "Администратор создан."}
if err := a.prepare.ExecuteTemplate(&b, "layout", d); err != nil {
t.Fatal(err)
}
_ = os.WriteFile(filepath.Join(dir, "prepare.html"), []byte(b.String()), 0o644)
}
// Первая страница (администратор) — для снимка в TestDumpOnboardingPages.
func TestDumpFirstRunPage(t *testing.T) {
dir := os.Getenv("BJ_DUMP_PAGES")
if dir == "" {
t.Skip("BJ_DUMP_PAGES не задан")
}
var b strings.Builder
if err := firstRunTpl.Execute(&b, firstRunData{NeedKey: true}); err != nil {
t.Fatal(err)
}
_ = os.WriteFile(filepath.Join(dir, "first-run.html"), []byte(b.String()), 0o644)
}