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 read = path => fs.readFileSync(require.resolve(`../src/${path}`), 'utf8'); function compile(source, globals = {}) { const exports = {}; const code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.ReactJSX } }).outputText; vm.runInNewContext(code, { exports, require, ...globals }); return exports; } function elements(root, predicate) { if (!React.isValidElement(root)) return []; return [...(predicate(root) ? [root] : []), ...React.Children.toArray(root.props.children).flatMap(child => elements(child, predicate))]; } const source = ts.createSourceFile('SettingsPage.tsx', read('pages/settings/SettingsPage.tsx'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); const page = source.statements.find(node => ts.isFunctionDeclaration(node) && node.name?.text === 'SettingsPage'); const CourseAiSettings = () => null; const { SettingsPage } = compile(page.getText(source), { CourseAiSettings, PageHeader: () => null, Bot: () => null }); const props = { session: { accessToken: 'test-token', user: { id: 'teacher' } }, selectedCourseId: 'course-a', courses: [{ id: 'course-a', title: 'A' }, { id: 'course-b', title: 'B' }], }; test('settings remounts the credential draft when course or session changes', () => { const form = input => elements(SettingsPage(input), node => node.type === CourseAiSettings)[0]; const initial = form(props); assert.equal(initial.props.selectedCourseId, 'course-a'); assert.notEqual(initial.key, form({ ...props, selectedCourseId: 'course-b' }).key); assert.notEqual(initial.key, form({ ...props, session: { ...props.session, accessToken: 'new-token' } }).key); }); test('settings is width-constrained and centered without a duplicate course selector', () => { const tree = SettingsPage(props); assert.match(tree.props.className, /mx-auto.*w-full.*max-w-\[920px\]/); const header = React.Children.toArray(tree.props.children)[0]; assert.equal(header.props.action, undefined); assert.equal(elements(tree, node => node.type === 'select').length, 0); assert.equal(elements(SettingsPage({ ...props, selectedCourseId: '', courses: [] }), node => node.type === CourseAiSettings).length, 0); }); test('curriculum no longer loads AI settings or offers an agent tab', () => { const curriculum = read('pages/curriculum/CurriculumPage.tsx'); assert.doesNotMatch(curriculum, /getCourseAiSetting|updateCourseAiSetting|handleSaveAiSetting|setActiveTab\('ai'\)/); assert.match(read('App.tsx'), //); assert.match(read('App.tsx'), /