68 lines
3.3 KiB
JavaScript
68 lines
3.3 KiB
JavaScript
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; });
|