From 0aa721dc0c59cb90d4a07f709aebdec3672612b4 Mon Sep 17 00:00:00 2001 From: jacaudi <47005674+jacaudi@users.noreply.github.com> Date: Sun, 12 Apr 2026 00:45:07 -0700 Subject: [PATCH 1/4] test: add regression tests for doStuff/undoStuff public entry points --- app/tests/test-file-ops-unification.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 app/tests/test-file-ops-unification.js diff --git a/app/tests/test-file-ops-unification.js b/app/tests/test-file-ops-unification.js new file mode 100644 index 0000000..68d3342 --- /dev/null +++ b/app/tests/test-file-ops-unification.js @@ -0,0 +1,24 @@ +const { describe, it } = require('node:test'); +const assert = require('node:assert'); +const fs = require('node:fs'); +const path = require('node:path'); + +const indexHtml = path.join(__dirname, '..', 'index.html'); + +describe('file-ops public entry points', () => { + it('built index.html declares doStuff as a top-level function', () => { + const html = fs.readFileSync(indexHtml, 'utf8'); + assert.ok( + /function\s+doStuff\s*\(/.test(html), + 'must contain function doStuff(...)' + ); + }); + + it('built index.html declares undoStuff as a top-level function', () => { + const html = fs.readFileSync(indexHtml, 'utf8'); + assert.ok( + /function\s+undoStuff\s*\(/.test(html), + 'must contain function undoStuff(...)' + ); + }); +}); From bf53f86771210187f0c22e6bedc794bb083c8e1e Mon Sep 17 00:00:00 2001 From: jacaudi <47005674+jacaudi@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:19:19 -0700 Subject: [PATCH 2/4] test: add failing FILE_OPS config table tests --- app/tests/test-file-ops-unification.js | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/app/tests/test-file-ops-unification.js b/app/tests/test-file-ops-unification.js index 68d3342..3d98a28 100644 --- a/app/tests/test-file-ops-unification.js +++ b/app/tests/test-file-ops-unification.js @@ -22,3 +22,51 @@ describe('file-ops public entry points', () => { ); }); }); + +describe('FILE_OPS config table (source-level)', () => { + const fileOpsSrc = fs.readFileSync( + path.join(__dirname, '..', '..', 'src', 'js', 'ui', 'file-ops.js'), + 'utf8' + ); + + it('defines FILE_OPS with enc and dec entries', () => { + assert.ok( + /const\s+FILE_OPS\s*=/.test(fileOpsSrc), + 'must declare FILE_OPS' + ); + assert.ok(fileOpsSrc.includes("enc:"), 'FILE_OPS must have enc entry'); + assert.ok(fileOpsSrc.includes("dec:"), 'FILE_OPS must have dec entry'); + }); + + it('enc entry uses encNewMsg and .filekey suffix', () => { + assert.ok(fileOpsSrc.includes('work: encNewMsg')); + assert.ok(fileOpsSrc.includes("'.filekey'") || fileOpsSrc.includes('".filekey"')); + }); + + it('dec entry uses decMsg and .filekey replacement', () => { + assert.ok(fileOpsSrc.includes('work: decMsg')); + assert.ok(/replace\s*\(\s*['"]\.filekey['"]/.test(fileOpsSrc)); + }); + + it('defines processFileBatch driver', () => { + assert.ok( + /function\s+processFileBatch\s*\(/.test(fileOpsSrc), + 'must declare processFileBatch' + ); + }); +}); + +describe('FILE_OPS filename transforms (extracted behavior)', () => { + const encSuffix = (name) => name + '.filekey'; + const decSuffix = (name) => name.replace('.filekey', ''); + + it('enc then dec restores original filename', () => { + assert.strictEqual(decSuffix(encSuffix('foo.txt')), 'foo.txt'); + assert.strictEqual(decSuffix(encSuffix('report.pdf')), 'report.pdf'); + assert.strictEqual(decSuffix(encSuffix('no-extension')), 'no-extension'); + }); + + it('dec is a no-op on names without .filekey', () => { + assert.strictEqual(decSuffix('plain.txt'), 'plain.txt'); + }); +}); From 7e248e5a504c27517e825a3765f37a16d0e4065e Mon Sep 17 00:00:00 2001 From: jacaudi <47005674+jacaudi@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:21:22 -0700 Subject: [PATCH 3/4] refactor: unify doStuff/undoStuff via FILE_OPS config table Co-Authored-By: Claude Opus 4.6 --- src/js/ui/file-ops.js | 112 ++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 64 deletions(-) diff --git a/src/js/ui/file-ops.js b/src/js/ui/file-ops.js index 6b22ec0..3bd7f66 100644 --- a/src/js/ui/file-ops.js +++ b/src/js/ui/file-ops.js @@ -248,40 +248,58 @@ function handleSharedFile(file_obj) { )(file_array[fc++]); } } -function undoStuff() { +const FILE_OPS = { + enc: { + work: encNewMsg, + transform: function(ret) { return combineArrayBuffers(ret.salt, ret.encrypted_buff); }, + filename: function(name) { return name + '.filekey'; }, + errorMsg: 'Failed to encrypt file. Please try again.', + encrypt_status: true, + }, + dec: { + work: decMsg, + transform: function(ret) { return ret.decrypted_buff; }, + filename: function(name) { return name.replace('.filekey', ''); }, + errorMsg: 'Failed to unlock file with this key. Please try again.', + encrypt_status: false, + }, +}; + +function processFileBatch(direction) { + const config = FILE_OPS[direction]; var file_array = current_active_file_array; - if (file_array.length > 0) { - let fc = 0; - let encrypt_status = false; - let status_obj; - status_obj = setStatusMsg(encrypt_status); - status_obj.animator = new set3dotStatusAnimation(status_obj); - (function nextFile(file) { - getFlatFile(file, function(file_contents, filename) { - decMsg(file_contents, function(ret) { - if (ret === null) { - status_obj.animator.clearStatus(); - var params = getErrorParams(); - var html_string = "Failed to unlock file with this key. Please try again."; - htmlWriter(params, html_string, main_inner); - } else { - var new_filename = filename.replace(".filekey", ""); - newDownloadObj(new_filename, ret.decrypted_buff, { - encrypt_status - }, function(ret) { - scrollForFirstFile(fc); - if (fc < file_array.length) - nextFile(file_array[fc++]); - else - status_obj.animator.triggerStatusFinish(status_obj); - }); - } + if (file_array.length === 0) return; + let fc = 0; + let status_obj = setStatusMsg(config.encrypt_status); + status_obj.animator = new set3dotStatusAnimation(status_obj); + (function nextFile(file) { + getFlatFile(file, function(file_contents, filename) { + config.work(file_contents, function(ret) { + if (ret === null) { + status_obj.animator.clearStatus(); + var html_string = '' + config.errorMsg + ''; + htmlWriter(getErrorParams(), html_string, main_inner); + return; + } + var out_buff = config.transform(ret); + var out_name = config.filename(filename); + newDownloadObj(out_name, out_buff, { + encrypt_status: config.encrypt_status + }, function(ret) { + scrollForFirstFile(fc); + if (fc < file_array.length) + nextFile(file_array[fc++]); + else + status_obj.animator.triggerStatusFinish(status_obj); }); }); - } - )(file_array[fc++]); - } + }); + })(file_array[fc++]); } + +function undoStuff() { processFileBatch('dec'); } + +function doStuff() { processFileBatch('enc'); } function getWarningParams() { return { char_speed: 2, @@ -339,37 +357,3 @@ function set3dotStatusAnimation(status_obj) { } } } -function doStuff() { - var file_array = current_active_file_array; - if (file_array.length > 0) { - let fc = 0; - let encrypt_status = true; - let status_obj; - status_obj = setStatusMsg(encrypt_status); - status_obj.animator = new set3dotStatusAnimation(status_obj); - (function nextFile(file) { - getFlatFile(file, function(file_contents, filename) { - encNewMsg(file_contents, function(ret) { - if (ret === null) { - status_obj.animator.clearStatus(); - var html_string = "Failed to encrypt file. Please try again."; - htmlWriter(getErrorParams(), html_string, main_inner); - return; - } - var combined_buff = combineArrayBuffers(ret.salt, ret.encrypted_buff); - var new_filename = filename + ".filekey"; - newDownloadObj(new_filename, combined_buff, { - encrypt_status - }, function(ret) { - scrollForFirstFile(fc); - if (fc < file_array.length) - nextFile(file_array[fc++]); - else - status_obj.animator.triggerStatusFinish(status_obj); - }); - }); - }); - } - )(file_array[fc++]); - } -} From b9f19e0c2e72a432b8c6a4470b163dd507a695ac Mon Sep 17 00:00:00 2001 From: jacaudi <47005674+jacaudi@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:08:12 -0700 Subject: [PATCH 4/4] refactor: tighten file_array binding to const in processFileBatch Co-Authored-By: Claude Opus 4.6 --- src/js/ui/file-ops.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ui/file-ops.js b/src/js/ui/file-ops.js index 3bd7f66..72ccc73 100644 --- a/src/js/ui/file-ops.js +++ b/src/js/ui/file-ops.js @@ -267,7 +267,7 @@ const FILE_OPS = { function processFileBatch(direction) { const config = FILE_OPS[direction]; - var file_array = current_active_file_array; + const file_array = current_active_file_array; if (file_array.length === 0) return; let fc = 0; let status_obj = setStatusMsg(config.encrypt_status);