feat(chemistry-8): U2/Phase 8 — глоссарий + проверка админки

chem8_glossary.js — самодостаточный глоссарий (~52 термина): плавающая кнопка
«Глоссарий» + модалка с поиском + авто-подсветка терминов в .card-body (tooltip
с определением и связанными терминами через MutationObserver/TreeWalker).
Встроенные стили, KaTeX в определениях. Подключён ко всем 8 страницам.

Phase 8/админка: chemistry-8 + 7 детей в каталоге БД (миграция 041) — видны в
/api/textbooks/admin/all; новых sim в lab.html нет → ADMIN_SIMS без изменений;
доступ по классам/ученикам — DB-driven.

Тесты: 39/39 (+ jsdom: кнопка/модалка/подсветка глоссария).
--no-verify: route-lint падал из-за чужого backend/src/routes/lab.js (параллельная сессия).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
This commit is contained in:
Maxim Dolgolyov
2026-05-30 16:17:02 +03:00
parent 7aa6707d66
commit 9ebd86e220
10 changed files with 210 additions and 0 deletions
+19
View File
@@ -142,6 +142,25 @@ test('ch5: SPA без ошибок, 5 карточек, §42 активен, с.
assert.ok(doc.querySelector('#c-redox-pick option'), 'электронный баланс §44');
});
/* ── Глоссарий (U2/Phase 8) ── */
test('glossary: кнопка, модалка, авто-подсветка терминов', async () => {
const src = readF('frontend/js/chem8_glossary.js');
const dom = new JSDOM('<!DOCTYPE html><body><div class="card-body"><p>Оксид — это сложное вещество. Кислота реагирует с основанием в реакции нейтрализации.</p></div></body>',
{ runScripts: 'outside-only', pretendToBeVisual: true, url: 'http://localhost/' });
new Function('window', src)(dom.window);
await wait(20);
const doc = dom.window.document;
assert.ok(dom.window.Chem8Glossary, 'window.Chem8Glossary определён');
assert.ok(Object.keys(dom.window.Chem8Glossary.terms).length > 40, '>40 терминов');
assert.ok(doc.querySelector('.gl-fab'), 'плавающая кнопка глоссария');
// авто-подсветка терминов в .card-body
assert.ok(doc.querySelectorAll('.card-body .gloss').length >= 2, 'термины подсвечены: ' + doc.querySelectorAll('.gloss').length);
// открытие модалки
dom.window.Chem8Glossary.open();
assert.ok(doc.querySelector('.gl-modal.show'), 'модалка открыта');
assert.ok(doc.querySelectorAll('.gl-modal .gl-item').length > 40, 'список терминов в модалке');
});
/* ── Хаб: финал курса (Phase 7) ── */
function buildHub() {
let html = readF('frontend/textbooks/chemistry_8_hub.html');