498d9a98e9
Complete the mandatory receiver-consent gate that was wired in the backend but never connected to the UI callback chain: - Add the missing onIncomingFileRequest (4th) callback to setFileTransferCallbacks in app.jsx — its absence caused handleFileTransferStart to auto-reject every incoming file. - Remove independent callback registration from FileTransferComponent; the component was overwriting app-level callbacks on mount and nulling all four on unmount, silently breaking progress/received/ error handlers whenever the panel was hidden. - Lift pendingIncomingFiles state to the root component so consent prompts are shown regardless of panel visibility; auto-open the panel on incoming request. - Add getReceivedFileObjectURL / revokeReceivedFileObjectURL on EnhancedSecureWebRTCManager for download buttons in the panel. - Update file-transfer-ui-cleanup regression test to match the new single-owner callback architecture. - All 14 tests pass; clean production build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
84 lines
3.0 KiB
JavaScript
84 lines
3.0 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import vm from 'node:vm';
|
|
|
|
const effects = [];
|
|
const setterCalls = [];
|
|
let stateIndex = 0;
|
|
const callbackCalls = [];
|
|
|
|
const context = {
|
|
window: {},
|
|
React: {
|
|
useState(initialValue) {
|
|
const index = stateIndex++;
|
|
return [initialValue, value => setterCalls.push({ index, value })];
|
|
},
|
|
useRef(initialValue) {
|
|
return { current: initialValue };
|
|
},
|
|
useEffect(effect) {
|
|
effects.push(effect);
|
|
},
|
|
createElement() {
|
|
return null;
|
|
}
|
|
}
|
|
};
|
|
|
|
const source = fs.readFileSync(new URL('../src/components/ui/FileTransfer.jsx', import.meta.url), 'utf8');
|
|
vm.runInNewContext(source, context);
|
|
|
|
const manager = {
|
|
fileTransferSystem: {
|
|
onProgress: () => {},
|
|
onFileReceived: () => {},
|
|
onError: () => {},
|
|
onIncomingFileRequest: () => {}
|
|
},
|
|
setFileTransferCallbacks(...args) {
|
|
callbackCalls.push(args);
|
|
this.onFileProgress = args[0];
|
|
this.onFileReceived = args[1];
|
|
this.onFileError = args[2];
|
|
this.onIncomingFileRequest = args[3];
|
|
if (this.fileTransferSystem) {
|
|
this.fileTransferSystem.onProgress = args[0];
|
|
this.fileTransferSystem.onFileReceived = args[1];
|
|
this.fileTransferSystem.onError = args[2];
|
|
this.fileTransferSystem.onIncomingFileRequest = args[3];
|
|
}
|
|
},
|
|
getFileTransfers() {
|
|
return { sending: [], receiving: [] };
|
|
},
|
|
isConnected() {
|
|
return false;
|
|
},
|
|
isVerified: false
|
|
};
|
|
|
|
// Component no longer manages callbacks — consent is handled by the parent (app.jsx).
|
|
// pendingIncomingFiles and onIncomingDecision are passed as props.
|
|
context.window.FileTransferComponent({ webrtcManager: manager, isConnected: false, pendingIncomingFiles: [], onIncomingDecision: null });
|
|
const cleanups = effects.map(effect => effect()).filter(Boolean);
|
|
|
|
// State index 0 = dragOver, index 1 = transfers.
|
|
// Transfers state should be reset to empty on disconnect.
|
|
assert.ok(setterCalls.some(call => call.index === 1 && call.value.sending.length === 0 && call.value.receiving.length === 0));
|
|
|
|
// Component must NOT call setFileTransferCallbacks — that is the parent's responsibility.
|
|
assert.equal(callbackCalls.length, 0, 'FileTransferComponent must not register its own callbacks');
|
|
|
|
// Cleanup effects must not null-out the manager's callbacks either.
|
|
cleanups.forEach(cleanup => cleanup());
|
|
assert.equal(callbackCalls.length, 0, 'cleanup must not call setFileTransferCallbacks');
|
|
|
|
// fileTransferSystem callbacks are untouched by the component.
|
|
assert.equal(typeof manager.fileTransferSystem.onProgress, 'function');
|
|
assert.equal(typeof manager.fileTransferSystem.onFileReceived, 'function');
|
|
assert.equal(typeof manager.fileTransferSystem.onError, 'function');
|
|
assert.equal(typeof manager.fileTransferSystem.onIncomingFileRequest, 'function');
|
|
|
|
console.log('File transfer UI cleanup tests passed');
|