const { test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const vm = require('node:vm'); const ts = require('typescript'); const React = require('react'); const { renderToStaticMarkup } = require('react-dom/server'); test('course detail opens a pinned lecture without a platform connection', () => { const source = fs.readFileSync(require.resolve('../src/renderer/src/features/curriculum/CurriculumDetailPanel.tsx'), 'utf8'); const exports = {}; const output = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.ReactJSX, esModuleInterop: true } }).outputText; vm.runInNewContext(output, { exports, require: name => name === '../../i18n' ? { t: value => value, useLocaleRender() {} } : require(name) }); const props = { week: { week: 1, title: '제어', topics: ['속도'], detail: { overview: '개요', topicDetails: ['설명'], deliverable: '확인', learningSteps: [{ lectureVersionId: 'pinned-v1' }] } }, selected: true, activeLearningStep: '속도' }; const html = renderToStaticMarkup(React.createElement(exports.default, { ...props, onOpenLecture() {} })); assert.match(html, /슬라이드·퀴즈 열기/); assert.doesNotMatch(renderToStaticMarkup(React.createElement(exports.default, props)), /슬라이드·퀴즈 열기/); }); test('quiz sends answers only and maintains a stable request ID for retry', () => { const source = fs.readFileSync(require.resolve('../src/renderer/src/features/curriculum/LectureViewerModal.tsx'), 'utf8'); assert.match(source, /\/quiz/); assert.match(source, /questionId: q.id/); assert.match(source, /optionIndex: answers\[q.id\]/); assert.match(source, /requestId.current \?\?= crypto.randomUUID/); assert.match(source, /signal: controller.current.signal/); assert.doesNotMatch(source, /score:.*answers/); }); test('lecture controls have English translations', () => { const en = require('../src/renderer/src/i18n/en.json'); for (const key of ['슬라이드·퀴즈 열기', '강의 닫기', '슬라이드', '퀴즈', '답안 제출', '다시 풀기']) assert.ok(en[key], key); }); test('presentation slides render key messages, examples and legacy body as safe text', () => { const source = fs.readFileSync(require.resolve('../src/renderer/src/features/curriculum/LectureSlideCanvas.tsx'), 'utf8'); const exports = {}; const output = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.ReactJSX, esModuleInterop: true } }).outputText; vm.runInNewContext(output, { exports, require: name => name === '../../i18n' ? { t: value => value } : require(name) }); const html = renderToStaticMarkup(React.createElement(exports.default, { slide: { id: 's1', type: 'content', title: '속도', subtitle: '핵심 메시지', body: '기존 본문', bullets: ['요점'], example: { label: '실습 예시', content: '' } }, title: '제어 강의', index: 0, total: 4 })); assert.match(html, /핵심 메시지/); assert.match(html, /기존 본문/); assert.match(html, /실습 예시/); assert.doesNotMatch(html, /