Files
Learn_System/backend/tests/math6-page.test.js
T
Maxim Dolgolyov 1d95f72d45 feat(math6): Phase 0 — инфраструктура учебника «Математика 6»
Хаб + 6 каркасов глав на общем движке math6_engine.js (плумбинг:
прогресс/XP/ачивки/навигация/сайдбар/поиск/глоссарий + хелперы),
math6_svg.js (window.Math6: numberLine, plane), math6.css (фреймворк
по образцу Алгебры 7). Миграция 049: хаб math-6 + math-6-ch1..ch6.
Секции глав генерируются движком из M6.paras; § без билдера → заглушка.
Тест math6-page.test.js: 8/8 (хаб 6 карточек, 6 глав, навигация, прогресс).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:47:21 +03:00

89 lines
4.2 KiB
JavaScript

'use strict';
/*
* Phase 0 jsdom-каркас «Математика 6»: хаб и 6 глав выполняются на движке
* math6_engine.js без ошибок скриптов; para-selector строится с нужным числом
* карточек; активен первый §; движок рендерит секции и заглушку/контент с кнопкой
* прочтения. Содержание §§ наполняется по главам — здесь проверяется каркас.
*/
const test = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
const { JSDOM, VirtualConsole } = require('jsdom');
const ROOT = path.join(__dirname, '..', '..');
const readF = p => fs.readFileSync(path.join(ROOT, p), 'utf8');
const wait = ms => new Promise(r => setTimeout(r, ms));
/* Инлайним внешние скрипты (CDN убираем, api/xp заменяем заглушками). */
function buildPage(file) {
let html = readF('frontend/textbooks/' + file);
const inl = {
'/js/math6_svg.js': readF('frontend/js/math6_svg.js'),
'/js/math6_engine.js': readF('frontend/js/math6_engine.js')
};
html = html
.replace(/<script defer src="https:\/\/cdn[^"]*"[^>]*><\/script>/g, '')
.replace(/<script src="\/js\/api\.js" defer><\/script>/, '<script>window.renderMathInElement=function(){};</script>')
.replace(/<script src="\/js\/xp\.js" defer><\/script>/, '');
Object.keys(inl).forEach(src => {
html = html.replace(new RegExp('<script src="' + src + '" defer><\\/script>'), () => '<script>\n' + inl[src] + '\n</script>');
});
return html;
}
async function loadDom(file) {
const errors = [];
const vc = new VirtualConsole();
vc.on('jsdomError', e => errors.push(e.message));
const dom = new JSDOM(buildPage(file), {
runScripts: 'dangerously', pretendToBeVisual: true, virtualConsole: vc, url: 'http://localhost/',
beforeParse(w) { w.scrollTo = function () {}; }
});
await wait(160);
return { dom, errors, doc: dom.window.document };
}
const CHAPTERS = [
{ file: 'math_6_ch1.html', cards: 12 },
{ file: 'math_6_ch2.html', cards: 9 },
{ file: 'math_6_ch3.html', cards: 5 },
{ file: 'math_6_ch4.html', cards: 11 },
{ file: 'math_6_ch5.html', cards: 5 },
{ file: 'math_6_ch6.html', cards: 6 }
];
for (const ch of CHAPTERS) {
test(`${ch.file}: SPA без ошибок, ${ch.cards} карточек, активен § 1`, async () => {
const { doc, errors } = await loadDom(ch.file);
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
assert.equal(doc.querySelectorAll('#psel-grid .psel-card').length, ch.cards, ch.cards + ' карточек');
const active = doc.querySelector('.sec.active');
assert.ok(active && active.id === 'sec-p1', 'активен sec-p1');
const body = doc.querySelector('#p1-body');
assert.ok(body && body.children.length > 0, 'тело § 1 заполнено');
assert.ok(doc.querySelector('#p1-body [data-read]'), 'кнопка прочтения § 1');
/* финал помечен и присутствует */
assert.ok(doc.querySelector('#psel-grid .psel-card.final'), 'есть карточка финала');
});
}
test('hub: 6 карточек глав', async () => {
const { doc, errors } = await loadDom('math_6_hub.html');
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
assert.equal(doc.querySelectorAll('.ch-grid .ch-card').length, 6, '6 глав');
});
test('навигация и прогресс: переход на § и отметка прочтения', async () => {
const { doc, errors } = await loadDom('math_6_ch1.html');
const win = doc.defaultView;
win.goTo('p4'); await wait(60);
assert.ok(doc.querySelector('#sec-p4.active'), 'перешли на § 4');
/* отметка прочтения начисляет XP */
const btn = doc.querySelector('#p4-body [data-read]');
assert.ok(btn, 'кнопка прочтения § 4');
btn.click(); await wait(20);
assert.ok((win.M6STATE.progress.p4 || 0) >= 30, 'прогресс § 4 вырос после прочтения');
assert.deepEqual(errors, [], 'нет ошибок: ' + errors.join(' | '));
});