Files
securebit-chat/tests/file-transfer-ui-cleanup.test.mjs
T
lockbitchat 498d9a98e9
CodeQL Analysis / Analyze CodeQL (push) Has been cancelled
Deploy Application / deploy (push) Has been cancelled
Mirror to Codeberg / mirror (push) Has been cancelled
Mirror to PrivacyGuides / mirror (push) Has been cancelled
release: v4.8.8 file transfer consent fix
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>
2026-05-26 22:40:36 -04:00

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');