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'); // Exercise effect cleanup and asynchronous IPC responses without starting Electron. const slots = []; let cursor = 0; let effects = []; const react = { useRef(value) { const i = cursor++; return slots[i] ??= { current: value }; }, useState(value) { const i = cursor++; if (!(i in slots)) slots[i] = value; return [slots[i], next => { slots[i] = typeof next === 'function' ? next(slots[i]) : next; }]; }, useEffect(fn, deps) { const i = cursor++; const previous = slots[i]; if (previous && deps.every((dep, index) => Object.is(dep, previous.deps[index]))) return; effects.push(() => { previous?.cleanup?.(); slots[i] = { deps, cleanup: fn() }; }); } }; const requests = []; const context = { exports: {}, require: name => { assert.equal(name, 'react'); return react; }, window: { remoteWorkspace: { moduleFiles: { content: (...args) => new Promise((resolve, reject) => requests.push({ args, resolve, reject })) } } } }; const source = fs.readFileSync(path.join(__dirname, '../src/renderer/src/features/workspace/useRemoteEditorFile.ts'), 'utf8'); vm.runInNewContext(ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText, context); const options = { enabled: true, targetKey: 'platform', getTarget: () => ({}), module: { name: 'Test', folderName: 'test' }, path: null, files: {}, dirty: {}, setFiles(fn) { options.files = fn(options.files); }, setSaved(fn) { options.saved = fn(options.saved ?? {}); } }; function render() { cursor = 0; const result = context.exports.useRemoteEditorFile(options); const pending = effects; effects = []; pending.forEach(fn => fn()); return result; } const tick = () => new Promise(resolve => setImmediate(resolve)); (async () => { render(); assert.equal(requests.length, 0); options.path = 'modules/Test/first.py'; assert.equal(render().loading, true); assert.equal(requests.length, 1); render(); assert.equal(requests.length, 1); options.path = 'modules/Test/second.py'; render(); requests[0].resolve({ content: 'old response' }); await tick(); assert.equal(options.files['modules/Test/first.py'], undefined); requests[1].resolve({ content: 'second' }); await tick(); assert.equal(render().loading, false); assert.equal(options.files[options.path], 'second'); options.path = null; render(); options.path = 'modules/Test/second.py'; render(); assert.equal(requests.length, 2); options.path = 'modules/Test/fail.py'; render(); requests[2].reject(new Error('offline')); await tick(); assert.ok(render().error); render().retry(); render(); assert.equal(requests.length, 4); options.dirty[options.path] = true; options.files[options.path] = 'unsaved'; render(); requests[3].resolve({ content: 'server copy' }); await tick(); assert.equal(options.files[options.path], 'unsaved'); options.path = 'modules/Test/image.png'; render(); assert.equal(requests.length, 4); options.enabled = false; options.path = 'modules/Test/local.py'; render(); assert.equal(requests.length, 4); console.log('Remote editor click loading, caching, cancellation, retry and dirty-file protection tests passed.'); })().catch(error => { console.error(error); process.exitCode = 1; });