const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const vm = require('node:vm') const ts = require('typescript') const root = path.resolve(__dirname, '../src/renderer/src/features/workspace') function setup(saved, blocked = false) { const storage = new Map([['autonomy-studio.lms.studio-theme', saved]]) const events = new Map() const exports = {} const code = ts.transpileModule(fs.readFileSync(path.join(root, 'studioTheme.ts'), 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText vm.runInNewContext(code, { exports, require: () => ({ useSyncExternalStore: (_subscribe, snapshot) => snapshot() }), localStorage: { getItem(key) { if (blocked) throw Error('blocked'); return storage.get(key) }, setItem(key, value) { if (blocked) throw Error('blocked'); storage.set(key, value) } }, window: { addEventListener: (type, listener) => events.set(type, listener) } }) return { api: exports, storage, events } } test('theme defaults dark, persists light, and restores the preference', () => { const { api, storage } = setup('invalid') assert.equal(api.useStudioTheme(), 'dark') api.setStudioTheme('light') assert.equal(api.useStudioTheme(), 'light') assert.equal(storage.get(api.STUDIO_THEME_KEY), 'light') assert.equal(setup('light').api.useStudioTheme(), 'light') }) test('theme remains usable when storage is blocked', () => { const { api } = setup(null, true) api.setStudioTheme('light') assert.equal(api.useStudioTheme(), 'light') }) test('theme synchronizes across windows without writing runtime settings', () => { const { api, storage, events } = setup('dark') storage.set(api.STUDIO_THEME_KEY, 'light') events.get('storage')({ key: api.STUDIO_THEME_KEY }) assert.equal(api.useStudioTheme(), 'light') storage.clear() events.get('storage')({ key: null }) assert.equal(api.useStudioTheme(), 'dark') }) test('module content horizontal scrollbar uses the shared workspace scrollbar style', () => { const source = ts.createSourceFile('ModuleListView.tsx', fs.readFileSync(path.join(root, 'ModuleListView.tsx'), 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX) const scrollClasses = [] function visit(node) { if (ts.isJsxAttribute(node) && node.name.getText(source) === 'className' && node.initializer && ts.isStringLiteral(node.initializer)) { const classes = node.initializer.text.split(/\s+/) if (classes.includes('overflow-x-auto')) scrollClasses.push(classes) } ts.forEachChild(node, visit) } visit(source) assert.ok(scrollClasses.length > 0) for (const classes of scrollClasses) assert.ok(classes.includes('thin-x-scrollbar')) })