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>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
-- Math 6 hub migration.
|
||||
-- Creates math-6 as a full hub textbook (6 chapters) in the style of chemistry-7 / algebra-7:
|
||||
-- math-6 (hub, html_path = math_6_hub.html)
|
||||
-- math-6-ch1 (Десятичные дроби, §§1–12) → math_6_ch1.html
|
||||
-- math-6-ch2 (Проценты и пропорции, §§1–9) → math_6_ch2.html
|
||||
-- math-6-ch3 (Множество, §§1–5) → math_6_ch3.html
|
||||
-- math-6-ch4 (Рациональные числа, §§1–11) → math_6_ch4.html
|
||||
-- math-6-ch5 (Координатная плоскость, §§1–5) → math_6_ch5.html
|
||||
-- math-6-ch6 (Наглядная геометрия, §§1–5) → math_6_ch6.html
|
||||
--
|
||||
-- Source: Герасимов В. Д., Пирютко О. Н., «Математика. 6 класс»,
|
||||
-- Минск: Адукацыя і выхаванне, 2022 (2-е изд.). Контент авторский (наш).
|
||||
-- Author left empty per project policy.
|
||||
|
||||
-- 1. Parent hub row.
|
||||
INSERT OR IGNORE INTO textbooks
|
||||
(slug, subject, grade, title, author, description, html_path, para_count, color, sort_order, is_active, parent_slug)
|
||||
VALUES
|
||||
('math-6', 'math', 6, 'Математика — 6 класс',
|
||||
'',
|
||||
'Полный курс математики 6 класса: десятичные дроби, проценты и пропорции, множества, рациональные (положительные и отрицательные) числа, координатная плоскость и наглядная геометрия. 6 глав, 38 параграфов, интерактивные тренажёры и финалы-боссы.',
|
||||
'math_6_hub.html', 48, 'indigo', 6, 1, NULL);
|
||||
|
||||
-- 2. Six chapters.
|
||||
INSERT OR IGNORE INTO textbooks
|
||||
(slug, subject, grade, title, author, description, html_path, para_count, color, sort_order, is_active, parent_slug)
|
||||
VALUES
|
||||
('math-6-ch1', 'math', 6, 'Математика 6 · Десятичные дроби',
|
||||
'',
|
||||
'§§1–12: десятичная запись и разряды, сравнение и округление, координатный луч, сложение и вычитание, умножение и деление (в т. ч. на 10, 100, 1000), деление на десятичную дробь, конечные и бесконечные дроби, преобразования выражений.',
|
||||
'math_6_ch1.html', 12, 'indigo', 1, 1, 'math-6'),
|
||||
('math-6-ch2', 'math', 6, 'Математика 6 · Проценты и пропорции',
|
||||
'',
|
||||
'§§1–9: проценты, три основные задачи на проценты, пропорция и её основное свойство, прямая и обратная пропорциональные зависимости, решение задач пропорцией, масштаб, круговые диаграммы.',
|
||||
'math_6_ch2.html', 9, 'cyan', 2, 1, 'math-6'),
|
||||
('math-6-ch3', 'math', 6, 'Математика 6 · Множество',
|
||||
'',
|
||||
'§§1–5: множество и его элементы, пустое множество, способы задания множеств, операции (пересечение и объединение), круги Эйлера и решение задач с их помощью.',
|
||||
'math_6_ch3.html', 5, 'violet', 3, 1, 'math-6'),
|
||||
('math-6-ch4', 'math', 6, 'Математика 6 · Рациональные числа',
|
||||
'',
|
||||
'§§1–11: положительные и отрицательные числа, координатная прямая, модуль и противоположные числа, множества Z и Q, сравнение, сложение, вычитание, умножение и деление рациональных чисел, законы сложения.',
|
||||
'math_6_ch4.html', 11, 'rose', 4, 1, 'math-6'),
|
||||
('math-6-ch5', 'math', 6, 'Математика 6 · Координатная плоскость',
|
||||
'',
|
||||
'§§1–5: прямоугольная (декартова) система координат, графики реальных процессов, графики прямой и обратной пропорциональной зависимости.',
|
||||
'math_6_ch5.html', 5, 'emerald', 5, 1, 'math-6'),
|
||||
('math-6-ch6', 'math', 6, 'Математика 6 · Наглядная геометрия',
|
||||
'',
|
||||
'§§1–5: наглядные представления тел и их развёртки, окружность и круг (длина окружности и площадь круга), виды треугольников, центральная и осевая симметрия.',
|
||||
'math_6_ch6.html', 6, 'amber', 6, 1, 'math-6');
|
||||
@@ -0,0 +1,88 @@
|
||||
'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(' | '));
|
||||
});
|
||||
Reference in New Issue
Block a user