44 lines
3.2 KiB
JavaScript
44 lines
3.2 KiB
JavaScript
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: '<img src=x onerror=alert(1)>' } }, title: '제어 강의', index: 0, total: 4 }));
|
|
assert.match(html, /핵심 메시지/); assert.match(html, /기존 본문/); assert.match(html, /실습 예시/);
|
|
assert.doesNotMatch(html, /<img/); assert.match(html, /<img/);
|
|
});
|