Compare commits

...

52 Commits

Author SHA1 Message Date
rugk
a96508afa5 fix(discussionViewer): fix "Open comment" button 2026-03-29 14:43:50 +00:00
rugk
2472ae0941 chore: update SRI hash 2026-03-26 10:34:40 +00:00
rugk
64db447a17 style: fix JSDoc types 2026-03-26 10:34:29 +00:00
rugk
96786cd532 style: properly format HTML in DiscussionViewer test 2026-03-26 10:34:20 +00:00
rugk
e207f5a0b7 test: remove not required classes 2026-03-25 17:40:32 +00:00
rugk
0de2b52550 chore: improve/guard function if invalid paameter is given 2026-03-25 17:39:20 +00:00
rugk
a1b54fda25 test: fix actual tets problem not using/having a loadig indicator in HTML set 2026-03-25 17:38:54 +00:00
rugk
5acdafaa23 wipfix(devcontainer): fix/use new URL for mocha feature 2026-03-20 16:52:06 +00:00
rugk
a02257a0a3 test: remove asliased cleanup function 2026-03-20 10:22:19 +00:00
rugk
7d09d66b06 test: strengthen PasteStatus test 2026-03-20 09:06:55 +00:00
rugk
13ecf7e7cb test: fix PasteStatus tests by creating pastesuccess notification 2026-03-20 09:06:35 +00:00
rugk
c2bfcc17fd test: explicitly include fs dependency 2026-03-20 08:16:04 +00:00
rugk
045a356c09 test: remove one more jQuery call 2026-03-19 17:23:09 +00:00
rugk
543dc39202 test: remove jQuery from CopyToClipboard even more
Co-Authored-By: Copilot
2026-03-19 17:18:17 +00:00
rugk
fbd697245d refactor: remove jQuery from initializers 2026-03-19 17:15:46 +00:00
rugk
e094c39186 fix: fix nullable problems found via AttachmentViewer test 2026-03-19 17:03:09 +00:00
rugk
784f381e28 test: change all other tests to not use jQuery 2026-03-19 16:53:30 +00:00
rugk
414a87c025 test: use native non jquery for TopNav too fixing most tests of it 2026-03-18 16:09:46 +00:00
rugk
cc431a3f14 test: use document.documentElement.innerHTML for setting test HTML 2026-03-18 14:09:15 +00:00
rugk
3820fdf765 style: remove manual cleanup 2026-03-18 13:58:49 +00:00
rugk
0f3831b423 style: use web APIs to set document instead of jQuery for compatibility 2026-03-18 13:58:24 +00:00
rugk
2cf32c1c75 style(jsdoc): fix typing 2026-03-18 13:57:21 +00:00
rugk
1104873830 test: fix tests error again 2026-03-18 13:57:09 +00:00
rugk
2678d2c2b2 test: fix test & implementation error with missing buttons (ids) in UI 2026-03-03 11:08:45 +00:00
rugk
3de6dc9f68 test: use cleanup function everywhere instead of jsdom manually 2026-03-03 11:01:30 +00:00
rugk
778df44b0b test: manually remove useless cleanup stuff 2026-03-03 11:00:25 +00:00
rugk
69533d493b Merge branch 'master' of https://github.com/PrivateBin/PrivateBin into js/removeJQuery 2026-03-03 10:28:27 +00:00
rugk
dbd380332c style: fix ESLint no-return-assign 2026-03-03 10:12:32 +00:00
rugk
d5bf7674ff style: use template literal 2026-03-03 10:07:52 +00:00
rugk
3a08f1866d chore: use correct base href 2026-03-03 10:02:22 +00:00
rugk
194eb93d00 chore: stay on ES2018 for now 2026-03-03 10:00:48 +00:00
rugk
bbeab997c4 chore: ignore jsconfig.json when exporting 2026-03-03 09:58:48 +00:00
rugk
0155f99e58 test: import privatebin.js 2026-03-01 18:41:16 +00:00
rugk
47aa7aae36 test: Copilot generated new simpler test cases 2026-03-01 16:30:07 +00:00
rugk
b11541a8e4 test: more test fixes
Co-Authored-By: Copliot
2026-03-01 16:19:34 +00:00
rugk
ea4f5ba920 test: Copilots tries at fixing tests 2026-03-01 15:22:05 +00:00
rugk
48678638d0 refactor: more null checks introduced by Copilot for fixing tests 2026-03-01 15:20:20 +00:00
rugk
71c6efd3bb wipfeat(i18n): improve error logging for missing translation 2026-03-01 15:16:05 +00:00
rugk
8d360cd6be wipfix: fix more nullabe problems 2026-03-01 15:05:44 +00:00
rugk
38baa8edbe wipfix(i18n): make sure to check for string
Likely introdued in https://github.com/PrivateBin/PrivateBin/pull/1730
2026-03-01 15:04:22 +00:00
rugk
393eb070e9 fix: manually fix exceptions occuring when loading main page 2026-03-01 14:58:18 +00:00
rugk
8bca112251 wipfix: fix variable shadowing problem 2026-03-01 14:29:38 +00:00
rugk
5b008ac5d9 refactor: remove more jQuery references 2026-03-01 14:26:41 +00:00
rugk
d8b377dd80 style: remove JQuery fallback 2026-03-01 15:03:16 +01:00
rugk
a4ca27d9a3 Potential fix for pull request finding 'Duplicate character in character class'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-02-26 17:57:04 +01:00
rugk
7528639fde Potential fix for pull request finding 'Duplicate character in character class'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-02-26 17:55:31 +01:00
rugk
d285a9dfee refactor: remove more jQuery
Co-Authored-By: CoPilot
2026-02-25 20:04:32 +00:00
rugk
84e7cbdfbf test: guard due to test error (fatal memory crash)
Co-Authored-By: Copilot
2026-02-25 18:58:50 +00:00
rugk
0fe51a91a4 test: add npm lint command 2026-02-25 18:57:30 +00:00
rugk
a6e250f825 refactor: remove more jQuery stuff 2026-02-25 10:09:14 +00:00
rugk
14cba7f4c1 refactor: first step removing jQuery
You are a Frontend engineer specialized in refactoring legacy codebases.
Your task is to solve https://github.com/PrivateBin/PrivateBin/issues/198 aka refactor the JavaScript Frontend code to remove all uses of jQuery in order to modernize the codebase.

**Important notes:**
 - The file to modify is #file:privatebin.js.
 - The mocha tests should stay compatible and continue to run.
 - Do not refactor anything else except what is needed for removing jQuery.

Co-Authored-By: Copilot
2026-02-25 09:53:04 +00:00
rugk
e2d900eb02 test: add basic JSConfig to make VSCode type-check the JS file 2026-02-25 09:51:16 +00:00
24 changed files with 2117 additions and 1985 deletions

View File

@@ -36,7 +36,7 @@
} }
}, },
"features": { "features": {
"ghcr.io/devcontainers-contrib/features/mocha:2": {}, "ghcr.io/devcontainers-extra/features/mocha:2": {},
"ghcr.io/yassinedoghri/devcontainers/php-extensions-installer:1": { "ghcr.io/yassinedoghri/devcontainers/php-extensions-installer:1": {
"extensions": "gd" "extensions": "gd"
} }

1
.gitattributes vendored
View File

@@ -9,6 +9,7 @@ js/.nycrc.yml export-ignore
js/common.js export-ignore js/common.js export-ignore
js/eslint.config.js export-ignore js/eslint.config.js export-ignore
js/test/ export-ignore js/test/ export-ignore
js/jsconfig.json export-ignore
.codeclimate.yml export-ignore .codeclimate.yml export-ignore
.csslintrc export-ignore .csslintrc export-ignore
.devcontainer export-ignore .devcontainer export-ignore

View File

@@ -4,7 +4,46 @@
global.assert = require('assert'); global.assert = require('assert');
global.jsc = require('jsverify'); global.jsc = require('jsverify');
global.jsdom = require('jsdom-global'); global.jsdom = require('jsdom-global');
global.cleanup = global.jsdom(); // initial DOM environment created by jsdom-global
let currentCleanup = global.jsdom();
// wrap cleanup so that calling it recreates a fresh jsdom environment
global.cleanup = function (...args) {
// remove previous environment
if (typeof currentCleanup === 'function') {
currentCleanup();
}
// create a new jsdom environment
currentCleanup = global.jsdom(...args);
// Make sure window and document are available in global scope for module loading
// jsdom-global sets them, but we need to ensure they're accessible
if (typeof window === 'undefined') {
throw new Error('jsdom-global failed to set up window');
}
if (typeof document === 'undefined') {
throw new Error('jsdom-global failed to set up document');
}
// Clear module cache to ensure modules are re-evaluated with new jsdom environment
delete require.cache[require.resolve('./privatebin')];
delete require.cache[require.resolve('./legacy')];
require('./privatebin');
if (typeof PrivateBin === 'undefined') {
throw new Error('PrivateBin module did not load correctly');
}
// also re-export the PrivateBin namespace if available
if (typeof window !== 'undefined' && window.PrivateBin) {
global.PrivateBin = window.PrivateBin;
if (global.$) {
global.$.PrivateBin = window.PrivateBin;
}
}
return global.cleanup;
};
global.fs = require('fs'); global.fs = require('fs');
global.WebCrypto = require('@peculiar/webcrypto').Crypto; global.WebCrypto = require('@peculiar/webcrypto').Crypto;
@@ -12,14 +51,23 @@ global.WebCrypto = require('@peculiar/webcrypto').Crypto;
global.$ = global.jQuery = require('./jquery-3.7.1'); global.$ = global.jQuery = require('./jquery-3.7.1');
global.zlib = require('./zlib-1.3.1-2').zlib; global.zlib = require('./zlib-1.3.1-2').zlib;
require('./prettify'); require('./prettify');
global.prettyPrint = window.PR.prettyPrint; global.prettyPrint = window.PR ? window.PR.prettyPrint : function() {};
global.prettyPrintOne = window.PR.prettyPrintOne; global.prettyPrintOne = window.PR ? window.PR.prettyPrintOne : function() {};
global.showdown = require('./showdown-2.1.0'); global.showdown = require('./showdown-2.1.0');
global.DOMPurify = require('./purify-3.3.0'); global.DOMPurify = require('./purify-3.3.0');
global.baseX = require('./base-x-5.0.1').baseX; global.baseX = require('./base-x-5.0.1').baseX;
global.Legacy = require('./legacy').Legacy; global.Legacy = require('./legacy').Legacy;
require('./privatebin'); require('./privatebin');
// provide global access to the namespace so tests can reference it directly
if (typeof window !== 'undefined' && window.PrivateBin) {
global.PrivateBin = window.PrivateBin;
// keep the old jQuery alias around just in case some tests still use it
if (global.$) {
global.$.PrivateBin = window.PrivateBin;
}
}
// internal variables // internal variables
var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z'], 'n','o','p','q','r','s','t','u','v','w','x','y','z'],

22
js/jsconfig.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "es2018",
"module": "NodeNext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"checkJs": true,
"strict": true,
"noImplicitAny": false,
"forceConsistentCasingInFileNames": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"baseUrl": "./js",
"paths": {
"/*": ["./*"],
}
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

View File

@@ -16,6 +16,7 @@
"nyc": "^17.1.0" "nyc": "^17.1.0"
}, },
"scripts": { "scripts": {
"lint": "eslint",
"test": "mocha", "test": "mocha",
"ci-test": "mocha --reporter xunit --reporter-option output=mocha-results.xml" "ci-test": "mocha --reporter xunit --reporter-option output=mocha-results.xml"
}, },

File diff suppressed because it is too large Load Diff

View File

@@ -11,12 +11,11 @@ describe('Alert', function () {
icon = icon.join(''); icon = icon.join('');
message = message.join(''); message = message.join('');
const expected = '<div id="status">' + message + '</div>'; const expected = '<div id="status">' + message + '</div>';
$('body').html( document.body.innerHTML =
'<div id="status"></div>' '<div id="status"></div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showStatus(message, icon);
$.PrivateBin.Alert.showStatus(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -30,14 +29,13 @@ describe('Alert', function () {
'class="statusmessage alert alert-info"><span ' + 'class="statusmessage alert alert-info"><span ' +
'class="glyphicon glyphicon-info-sign" ' + 'class="glyphicon glyphicon-info-sign" ' +
'aria-hidden="true"></span> <span>' + message + '</span></div>'; 'aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="status" role="alert" class="statusmessage ' + '<div id="status" role="alert" class="statusmessage ' +
'alert alert-info hidden"><span class="glyphicon ' + 'alert alert-info hidden"><span class="glyphicon ' +
'glyphicon-info-sign" aria-hidden="true"></span> </div>' 'glyphicon-info-sign" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showStatus(message);
$.PrivateBin.Alert.showStatus(message); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -53,14 +51,13 @@ describe('Alert', function () {
'class="statusmessage alert alert-info"><span ' + 'class="statusmessage alert alert-info"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> <span>' + message + '</span></div>'; '" aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="status" role="alert" class="statusmessage ' + '<div id="status" role="alert" class="statusmessage ' +
'alert alert-info hidden"><span class="glyphicon ' + 'alert alert-info hidden"><span class="glyphicon ' +
'glyphicon-info-sign" aria-hidden="true"></span> </div>' 'glyphicon-info-sign" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showStatus(message, icon);
$.PrivateBin.Alert.showStatus(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -75,12 +72,11 @@ describe('Alert', function () {
icon = icon.join(''); icon = icon.join('');
message = message.join(''); message = message.join('');
const expected = '<div id="errormessage">' + message + '</div>'; const expected = '<div id="errormessage">' + message + '</div>';
$('body').html( document.body.innerHTML =
'<div id="errormessage"></div>' '<div id="errormessage"></div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showWarning(message, icon);
$.PrivateBin.Alert.showWarning(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -95,14 +91,13 @@ describe('Alert', function () {
'class="statusmessage alert alert-danger"><span ' + 'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-warning-sign" ' + 'class="glyphicon glyphicon-warning-sign" ' +
'aria-hidden="true"></span> <span>' + message + '</span></div>'; 'aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger hidden"><span class="glyphicon ' + 'alert alert-danger hidden"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>' 'glyphicon-alert" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showWarning(message);
$.PrivateBin.Alert.showWarning(message); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -118,14 +113,13 @@ describe('Alert', function () {
'class="statusmessage alert alert-danger"><span ' + 'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> <span>' + message + '</span></div>'; '" aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger hidden"><span class="glyphicon ' + 'alert alert-danger hidden"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>' 'glyphicon-alert" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showWarning(message, icon);
$.PrivateBin.Alert.showWarning(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -140,12 +134,11 @@ describe('Alert', function () {
icon = icon.join(''); icon = icon.join('');
message = message.join(''); message = message.join('');
const expected = '<div id="errormessage">' + message + '</div>'; const expected = '<div id="errormessage">' + message + '</div>';
$('body').html( document.body.innerHTML =
'<div id="errormessage"></div>' '<div id="errormessage"></div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showError(message, icon);
$.PrivateBin.Alert.showError(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -160,14 +153,13 @@ describe('Alert', function () {
'class="statusmessage alert alert-danger"><span ' + 'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-alert" ' + 'class="glyphicon glyphicon-alert" ' +
'aria-hidden="true"></span> <span>' + message + '</span></div>'; 'aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger hidden"><span class="glyphicon ' + 'alert alert-danger hidden"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>' 'glyphicon-alert" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showError(message);
$.PrivateBin.Alert.showError(message); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -183,14 +175,13 @@ describe('Alert', function () {
'class="statusmessage alert alert-danger"><span ' + 'class="statusmessage alert alert-danger"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> <span>' + message + '</span></div>'; '" aria-hidden="true"></span> <span>' + message + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger hidden"><span class="glyphicon ' + 'alert alert-danger hidden"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>' 'glyphicon-alert" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showError(message, icon);
$.PrivateBin.Alert.showError(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -206,12 +197,11 @@ describe('Alert', function () {
message = message.join(''); message = message.join('');
string = string.join(''); string = string.join('');
const expected = '<div id="remainingtime" class="">' + string + message + number + '</div>'; const expected = '<div id="remainingtime" class="">' + string + message + number + '</div>';
$('body').html( document.body.innerHTML =
'<div id="remainingtime" class="hidden"></div>' '<div id="remainingtime" class="hidden"></div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
$.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -228,14 +218,13 @@ describe('Alert', function () {
'class="alert alert-info"><span ' + 'class="alert alert-info"><span ' +
'class="glyphicon glyphicon-fire" aria-hidden="true">' + 'class="glyphicon glyphicon-fire" aria-hidden="true">' +
'</span> <span>' + string + message + number + '</span></div>'; '</span> <span>' + string + message + number + '</span></div>';
$('body').html( document.body.innerHTML =
'<div id="remainingtime" role="alert" class="hidden ' + '<div id="remainingtime" role="alert" class="hidden ' +
'alert alert-info"><span class="glyphicon ' + 'alert alert-info"><span class="glyphicon ' +
'glyphicon-fire" aria-hidden="true"></span> </div>' 'glyphicon-fire" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
$.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -254,12 +243,11 @@ describe('Alert', function () {
message = defaultMessage; message = defaultMessage;
} }
const expected = '<div id="loadingindicator" class="">' + message + '</div>'; const expected = '<div id="loadingindicator" class="">' + message + '</div>';
$('body').html( document.body.innerHTML =
'<div id="loadingindicator" class="hidden">' + defaultMessage + '</div>' '<div id="loadingindicator" class="hidden">' + defaultMessage + '</div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showLoading(message, icon);
$.PrivateBin.Alert.showLoading(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -279,15 +267,14 @@ describe('Alert', function () {
'id="loadingindicator" class="navbar-text"><span ' + 'id="loadingindicator" class="navbar-text"><span ' +
'class="glyphicon glyphicon-' + icon + 'class="glyphicon glyphicon-' + icon +
'" aria-hidden="true"></span> <span>' + message + '</span></li></ul>'; '" aria-hidden="true"></span> <span>' + message + '</span></li></ul>';
$('body').html( document.body.innerHTML =
'<ul class="nav navbar-nav"><li id="loadingindicator" ' + '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text hidden"><span class="glyphicon ' + 'class="navbar-text hidden"><span class="glyphicon ' +
'glyphicon-time" aria-hidden="true"></span> ' + 'glyphicon-time" aria-hidden="true"></span> ' +
defaultMessage + '</li></ul>' defaultMessage + '</li></ul>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.showLoading(message, icon);
$.PrivateBin.Alert.showLoading(message, icon); const result = document.body.innerHTML;
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -297,17 +284,16 @@ describe('Alert', function () {
it( it(
'hides the loading message', 'hides the loading message',
function() { function() {
$('body').html( document.body.innerHTML =
'<ul class="nav navbar-nav"><li id="loadingindicator" ' + '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text"><span class="glyphicon ' + 'class="navbar-text"><span class="glyphicon ' +
'glyphicon-time" aria-hidden="true"></span> ' + 'glyphicon-time" aria-hidden="true"></span> ' +
'Loading…</li></ul>' 'Loading…</li></ul>';
); document.body.classList.add('loading');
$('body').addClass('loading'); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.hideLoading();
$.PrivateBin.Alert.hideLoading(); assert.ok(!document.body.classList.contains('loading'));
assert.ok(!$('body').hasClass('loading')); assert.ok(document.getElementById('loadingindicator').classList.contains('hidden'));
assert.ok($('#loadingindicator').hasClass('hidden'));
} }
); );
}); });
@@ -316,18 +302,17 @@ describe('Alert', function () {
it( it(
'hides all messages', 'hides all messages',
function() { function() {
$('body').html( document.body.innerHTML =
'<div id="status" role="alert" class="statusmessage ' + '<div id="status" role="alert" class="statusmessage ' +
'alert alert-info"><span class="glyphicon ' + 'alert alert-info"><span class="glyphicon ' +
'glyphicon-info-sign" aria-hidden="true"></span> </div>' + 'glyphicon-info-sign" aria-hidden="true"></span> </div>' +
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger"><span class="glyphicon ' + 'alert alert-danger"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>' 'glyphicon-alert" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.hideMessages();
$.PrivateBin.Alert.hideMessages(); assert.ok(document.getElementById('status').classList.contains('hidden'));
assert.ok($('#status').hasClass('hidden')); assert.ok(document.getElementById('errormessage').classList.contains('hidden'));
assert.ok($('#errormessage').hasClass('hidden'));
} }
); );
}); });
@@ -342,15 +327,15 @@ describe('Alert', function () {
let handlerCalled = false, let handlerCalled = false,
defaultMessage = 'Loading…', defaultMessage = 'Loading…',
functions = [ functions = [
$.PrivateBin.Alert.showStatus, PrivateBin.Alert.showStatus,
$.PrivateBin.Alert.showError, PrivateBin.Alert.showError,
$.PrivateBin.Alert.showRemaining, PrivateBin.Alert.showRemaining,
$.PrivateBin.Alert.showLoading PrivateBin.Alert.showLoading
]; ];
if (message.length === 0) { if (message.length === 0) {
message = defaultMessage; message = defaultMessage;
} }
$('body').html( document.body.innerHTML =
'<ul class="nav navbar-nav"><li id="loadingindicator" ' + '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
'class="navbar-text hidden"><span class="glyphicon ' + 'class="navbar-text hidden"><span class="glyphicon ' +
'glyphicon-time" aria-hidden="true"></span> ' + 'glyphicon-time" aria-hidden="true"></span> ' +
@@ -363,15 +348,14 @@ describe('Alert', function () {
'glyphicon-info-sign" aria-hidden="true"></span> </div>' + 'glyphicon-info-sign" aria-hidden="true"></span> </div>' +
'<div id="errormessage" role="alert" class="statusmessage ' + '<div id="errormessage" role="alert" class="statusmessage ' +
'alert alert-danger"><span class="glyphicon ' + 'alert alert-danger"><span class="glyphicon ' +
'glyphicon-alert" aria-hidden="true"></span> </div>' 'glyphicon-alert" aria-hidden="true"></span> </div>';
); PrivateBin.Alert.init();
$.PrivateBin.Alert.init(); PrivateBin.Alert.setCustomHandler(function(id, element) {
$.PrivateBin.Alert.setCustomHandler(function(id, $element) {
handlerCalled = true; handlerCalled = true;
return jsc.random(0, 1) ? true : $element; return jsc.random(0, 1) ? true : element;
}); });
functions[trigger](message); functions[trigger](message);
$.PrivateBin.Alert.setCustomHandler(null); PrivateBin.Alert.setCustomHandler(null);
return handlerCalled; return handlerCalled;
} }
); );

View File

@@ -14,7 +14,7 @@ describe('AttachmentViewer', function () {
'string', 'string',
// eslint-disable-next-line complexity // eslint-disable-next-line complexity
function (mimeType, rawdata, filename, prefix, postfix) { function (mimeType, rawdata, filename, prefix, postfix) {
let clean = jsdom(), let clean = globalThis.cleanup(),
data = 'data:' + mimeType + ';base64,' + common.btoa(rawdata), data = 'data:' + mimeType + ';base64,' + common.btoa(rawdata),
mimePrefix = mimeType.substring(0, 6), mimePrefix = mimeType.substring(0, 6),
previewSupported = ( previewSupported = (
@@ -31,7 +31,7 @@ describe('AttachmentViewer', function () {
} }
prefix = prefix.replace(/%(s|d)/g, '%%'); prefix = prefix.replace(/%(s|d)/g, '%%');
postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<|>/g, ''); postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<|>/g, '');
$('body').html( document.body.innerHTML = (
'<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>' + '<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>' +
'<div id="attachment" class="hidden"></div>' + '<div id="attachment" class="hidden"></div>' +
'<div id="templates">' + '<div id="templates">' +
@@ -51,60 +51,62 @@ describe('AttachmentViewer', function () {
}} }}
); );
} }
$.PrivateBin.AttachmentViewer.init(); PrivateBin.AttachmentViewer.init();
$.PrivateBin.Model.init(); PrivateBin.Model.init();
results.push( results.push(
!$.PrivateBin.AttachmentViewer.hasAttachment() && !PrivateBin.AttachmentViewer.hasAttachment() &&
$('#attachment').hasClass('hidden') && document.getElementById('attachment').classList.contains('hidden') &&
$('#attachment').children().length === 0 && document.getElementById('attachment').children.length === 0 &&
$('#attachmenttemplate').hasClass('hidden') && document.getElementById('attachmenttemplate').classList.contains('hidden') &&
$('#attachmentPreview').hasClass('hidden') document.getElementById('attachmentPreview').classList.contains('hidden')
); );
global.atob = common.atob; global.atob = common.atob;
if (filename.length) { if (filename.length) {
$.PrivateBin.AttachmentViewer.setAttachment(data, filename); PrivateBin.AttachmentViewer.setAttachment(data, filename);
} else { } else {
$.PrivateBin.AttachmentViewer.setAttachment(data); PrivateBin.AttachmentViewer.setAttachment(data);
} }
// // beyond this point we will get the blob URL instead of the data // // beyond this point we will get the blob URL instead of the data
data = window.URL.createObjectURL(data); data = window.URL.createObjectURL(data);
const attachment = $.PrivateBin.AttachmentViewer.getAttachments(); const attachment = PrivateBin.AttachmentViewer.getAttachments();
results.push( results.push(
$.PrivateBin.AttachmentViewer.hasAttachment() && PrivateBin.AttachmentViewer.hasAttachment() &&
$('#attachment').hasClass('hidden') && document.getElementById('attachment').classList.contains('hidden') &&
$('#attachment').children().length > 0 && document.getElementById('attachment').children.length > 0 &&
$('#attachmentPreview').hasClass('hidden') && document.getElementById('attachmentPreview').classList.contains('hidden') &&
attachment[0][0] === data && attachment[0][0] === data &&
attachment[0][1] === filename attachment[0][1] === filename
); );
$.PrivateBin.AttachmentViewer.showAttachment(); PrivateBin.AttachmentViewer.showAttachment();
results.push( results.push(
!$('#attachment').hasClass('hidden') && !document.getElementById('attachment').classList.contains('hidden') &&
$('#attachment').children().length > 0 && document.getElementById('attachment').children.length > 0 &&
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden')) (previewSupported ? !document.getElementById('attachmentPreview').classList.contains('hidden') : document.getElementById('attachmentPreview').classList.contains('hidden'))
); );
$.PrivateBin.AttachmentViewer.hideAttachment(); PrivateBin.AttachmentViewer.hideAttachment();
results.push( results.push(
$('#attachment').hasClass('hidden') && document.getElementById('attachment').classList.contains('hidden') &&
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden')) (previewSupported ? !document.getElementById('attachmentPreview').classList.contains('hidden') : document.getElementById('attachmentPreview').classList.contains('hidden'))
); );
if (previewSupported) { if (previewSupported) {
$.PrivateBin.AttachmentViewer.hideAttachmentPreview(); PrivateBin.AttachmentViewer.hideAttachmentPreview();
results.push($('#attachmentPreview').hasClass('hidden')); results.push(document.getElementById('attachmentPreview').classList.contains('hidden'));
} }
$.PrivateBin.AttachmentViewer.showAttachment(); PrivateBin.AttachmentViewer.showAttachment();
results.push( results.push(
!$('#attachment').hasClass('hidden') && !document.getElementById('attachment').classList.contains('hidden') &&
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden')) (previewSupported ? !document.getElementById('attachmentPreview').classList.contains('hidden') : document.getElementById('attachmentPreview').classList.contains('hidden'))
); );
let element = $('<div>'); let element = document.createElement('div');
$.PrivateBin.AttachmentViewer.moveAttachmentTo(element, attachment[0], prefix + '%s' + postfix); PrivateBin.AttachmentViewer.moveAttachmentTo(element, attachment[0], prefix + '%s' + postfix);
// messageIDs with links get a relaxed treatment // messageIDs with links get a relaxed treatment
if (prefix.indexOf('<a') === -1 && postfix.indexOf('<a') === -1) { if (prefix.indexOf('<a') === -1 && postfix.indexOf('<a') === -1) {
result = $('<textarea>').text((prefix + filename + postfix)).text(); const tempTA = document.createElement('textarea');
tempTA.textContent = (prefix + filename + postfix);
result = tempTA.textContent;
} else { } else {
result = DOMPurify.sanitize( result = DOMPurify.sanitize(
prefix + $.PrivateBin.Helper.htmlEntities(filename) + postfix, { prefix + PrivateBin.Helper.htmlEntities(filename) + postfix, {
ALLOWED_TAGS: ['a', 'i', 'span'], ALLOWED_TAGS: ['a', 'i', 'span'],
ALLOWED_ATTR: ['href', 'id'] ALLOWED_ATTR: ['href', 'id']
} }
@@ -112,18 +114,18 @@ describe('AttachmentViewer', function () {
} }
if (filename.length) { if (filename.length) {
results.push( results.push(
element.find('a')[0].href === data && element.querySelector('a').href === data &&
element.find('a')[0].getAttribute('download') === filename && element.querySelector('a').getAttribute('download') === filename &&
element.find('a')[0].text === result element.querySelector('a').textContent === result
); );
} else { } else {
results.push(element.find('a')[0].href === data); results.push(element.querySelector('a').href === data);
} }
$.PrivateBin.AttachmentViewer.removeAttachment(); PrivateBin.AttachmentViewer.removeAttachment();
results.push( results.push(
$('#attachment').hasClass('hidden') && document.getElementById('attachment').classList.contains('hidden') &&
$('#attachment').children().length === 0 && document.getElementById('attachment').children.length === 0 &&
$('#attachmentPreview').hasClass('hidden') document.getElementById('attachmentPreview').classList.contains('hidden')
); );
clean(); clean();
return results.every(element => element); return results.every(element => element);
@@ -133,8 +135,8 @@ describe('AttachmentViewer', function () {
it( it(
'sanitizes file names in attachments', 'sanitizes file names in attachments',
function() { function() {
const clean = jsdom(); const clean = globalThis.cleanup();
$('body').html( document.body.innerHTML = (
'<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>' + '<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>' +
'<div id="attachment" class="hidden"></div>' + '<div id="attachment" class="hidden"></div>' +
'<div id="templates">' + '<div id="templates">' +
@@ -154,8 +156,8 @@ describe('AttachmentViewer', function () {
}} }}
); );
} }
$.PrivateBin.AttachmentViewer.init(); PrivateBin.AttachmentViewer.init();
$.PrivateBin.Model.init(); PrivateBin.Model.init();
global.atob = common.atob; global.atob = common.atob;
const maliciousFileNames = [ const maliciousFileNames = [
@@ -163,8 +165,8 @@ describe('AttachmentViewer', function () {
'"><meta http-equiv="refresh" content="0;url=http://example.com/">.txt' '"><meta http-equiv="refresh" content="0;url=http://example.com/">.txt'
]; ];
for (const filename of maliciousFileNames) { for (const filename of maliciousFileNames) {
$.PrivateBin.AttachmentViewer.setAttachment('data:;base64,', filename); PrivateBin.AttachmentViewer.setAttachment('data:;base64,', filename);
assert.ok(!$('body').html().includes(filename)); assert.ok(!document.body.innerHTML.includes(filename));
} }
clean(); clean();
} }

View File

@@ -12,7 +12,7 @@ describe('Check', function () {
jsc.elements(['Bot', 'bot']), jsc.elements(['Bot', 'bot']),
'string', 'string',
function (prefix, botBit, suffix) { function (prefix, botBit, suffix) {
const clean = jsdom( const clean = globalThis.cleanup(
'<html><body><div id="errormessage" class="hidden"></div>' + '<html><body><div id="errormessage" class="hidden"></div>' +
'</body></html>', { '</body></html>', {
'userAgent': prefix + botBit + suffix 'userAgent': prefix + botBit + suffix
@@ -36,7 +36,7 @@ describe('Check', function () {
function (secureProtocol, localhost, domain, tld) { function (secureProtocol, localhost, domain, tld) {
const isDomain = localhost === '', const isDomain = localhost === '',
isSecureContext = secureProtocol || !isDomain || tld.length > 0, isSecureContext = secureProtocol || !isDomain || tld.length > 0,
clean = jsdom( clean = globalThis.cleanup(
'<html><body><div id="errormessage" class="hidden"></div>' + '<html><body><div id="errormessage" class="hidden"></div>' +
'<div id="oldnotice" class="hidden"></div>' + '<div id="oldnotice" class="hidden"></div>' +
'<div id="insecurecontextnotice" class="hidden"></div></body></html>', '<div id="insecurecontextnotice" class="hidden"></div></body></html>',
@@ -63,7 +63,7 @@ describe('Check', function () {
'bool', 'bool',
jsc.nearray(common.jscA2zString()), jsc.nearray(common.jscA2zString()),
function (secureProtocol, domain) { function (secureProtocol, domain) {
const clean = jsdom( const clean = globalThis.cleanup(
'<html><body><div id="httpnotice" class="hidden"></div>' + '<html><body><div id="httpnotice" class="hidden"></div>' +
'</body></html>', '</body></html>',
{ {

View File

@@ -9,10 +9,10 @@ describe('CopyToClipboard', function() {
common.jscFormats(), common.jscFormats(),
'nestring', 'nestring',
async function (format, text) { async function (format, text) {
var clean = jsdom(); var clean = globalThis.cleanup();
common.enableClipboard(); common.enableClipboard();
$('body').html( document.body.innerHTML = (
'<div id="placeholder" class="hidden">+++ no document text ' + '<div id="placeholder" class="hidden">+++ no document text ' +
'+++</div><div id="prettymessage" class="hidden">' + '+++</div><div id="prettymessage" class="hidden">' +
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' + '<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
@@ -21,14 +21,14 @@ describe('CopyToClipboard', function() {
'</div><div id="plaintext" class="hidden"></div>' '</div><div id="plaintext" class="hidden"></div>'
); );
$.PrivateBin.PasteViewer.init(); PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format); PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text); PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run(); PrivateBin.PasteViewer.run();
$.PrivateBin.CopyToClipboard.init(); PrivateBin.CopyToClipboard.init();
$('#prettyMessageCopyBtn').trigger('click'); document.getElementById('prettyMessageCopyBtn').click();
const savedToClipboardText = await navigator.clipboard.readText(); const savedToClipboardText = await navigator.clipboard.readText();
@@ -46,10 +46,10 @@ describe('CopyToClipboard', function() {
common.jscFormats(), common.jscFormats(),
'nestring', 'nestring',
async function (format, text) { async function (format, text) {
var clean = jsdom(); var clean = globalThis.cleanup();
common.enableClipboard(); common.enableClipboard();
$('body').html( document.body.innerHTML = (
'<div id="placeholder">+++ no document text ' + '<div id="placeholder">+++ no document text ' +
'+++</div><div id="prettymessage" class="hidden">' + '+++</div><div id="prettymessage" class="hidden">' +
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' + '<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
@@ -58,14 +58,14 @@ describe('CopyToClipboard', function() {
'</div><div id="plaintext" class="hidden"></div>' '</div><div id="plaintext" class="hidden"></div>'
); );
$.PrivateBin.PasteViewer.init(); PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format); PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text); PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run(); PrivateBin.PasteViewer.run();
$.PrivateBin.CopyToClipboard.init(); PrivateBin.CopyToClipboard.init();
$('body').trigger('copy'); document.body.dispatchEvent(new Event('copy'));
const copiedTextWithoutSelectedText = await navigator.clipboard.readText(); const copiedTextWithoutSelectedText = await navigator.clipboard.readText();
@@ -80,15 +80,15 @@ describe('CopyToClipboard', function() {
jsc.property('Copy link to clipboard', jsc.property('Copy link to clipboard',
'nestring', 'nestring',
async function (text) { async function (text) {
var clean = jsdom(); var clean = globalThis.cleanup();
common.enableClipboard(); common.enableClipboard();
$('body').html('<button id="copyLink"></button>'); document.body.innerHTML = '<button id="copyLink"></button>';
$.PrivateBin.CopyToClipboard.init(); PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.setUrl(text); PrivateBin.CopyToClipboard.setUrl(text);
$('#copyLink').trigger('click'); document.getElementById('copyLink').click();
const copiedText = await navigator.clipboard.readText(); const copiedText = await navigator.clipboard.readText();
@@ -103,14 +103,14 @@ describe('CopyToClipboard', function() {
jsc.property('Show hint', jsc.property('Show hint',
'nestring', 'nestring',
function (text) { function (text) {
var clean = jsdom(); var clean = globalThis.cleanup();
$('body').html('<small id="copyShortcutHintText"></small>'); document.body.innerHTML = '<small id="copyShortcutHintText"></small>';
$.PrivateBin.CopyToClipboard.init(); PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.showKeyboardShortcutHint(); PrivateBin.CopyToClipboard.showKeyboardShortcutHint();
const keyboardShortcutHint = $('#copyShortcutHintText').text(); const keyboardShortcutHint = document.getElementById('copyShortcutHintText').textContent;
clean(); clean();
@@ -121,14 +121,14 @@ describe('CopyToClipboard', function() {
jsc.property('Hide hint', jsc.property('Hide hint',
'nestring', 'nestring',
function (text) { function (text) {
var clean = jsdom(); var clean = globalThis.cleanup();
$('body').html('<small id="copyShortcutHintText">' + text + '</small>'); document.body.innerHTML = '<small id="copyShortcutHintText">' + text + '</small>';
$.PrivateBin.CopyToClipboard.init(); PrivateBin.CopyToClipboard.init();
$.PrivateBin.CopyToClipboard.hideKeyboardShortcutHint(); PrivateBin.CopyToClipboard.hideKeyboardShortcutHint();
const keyboardShortcutHint = $('#copyShortcutHintText').text(); const keyboardShortcutHint = document.getElementById('copyShortcutHintText').textContent;
clean(); clean();
@@ -136,4 +136,4 @@ describe('CopyToClipboard', function() {
} }
); );
}); });
}); });

View File

@@ -1,5 +1,6 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const fs = require('fs');
describe('CryptTool', function () { describe('CryptTool', function () {
describe('cipher & decipher', function () { describe('cipher & decipher', function () {
@@ -15,9 +16,9 @@ describe('CryptTool', function () {
'string', 'string',
'string', 'string',
async function (key, password, message) { async function (key, password, message) {
const clean = jsdom(); const clean = globalThis.cleanup();
// ensure zlib is getting loaded // ensure zlib is getting loaded
$.PrivateBin.Controller.initZ(); PrivateBin.Controller.initZ();
Object.defineProperty(window, 'crypto', { Object.defineProperty(window, 'crypto', {
value: new WebCrypto(), value: new WebCrypto(),
writeable: false writeable: false
@@ -25,10 +26,10 @@ describe('CryptTool', function () {
global.atob = common.atob; global.atob = common.atob;
global.btoa = common.btoa; global.btoa = common.btoa;
message = message.trim(); message = message.trim();
const cipherMessage = await $.PrivateBin.CryptTool.cipher( const cipherMessage = await PrivateBin.CryptTool.cipher(
key, password, message, [] key, password, message, []
), ),
plaintext = await $.PrivateBin.CryptTool.decipher( plaintext = await PrivateBin.CryptTool.decipher(
key, password, cipherMessage key, password, cipherMessage
); );
clean(); clean();
@@ -42,19 +43,19 @@ describe('CryptTool', function () {
it('does not truncate messages', async function () { it('does not truncate messages', async function () {
const message = fs.readFileSync('test/compression-sample.txt', 'ascii').trim(), const message = fs.readFileSync('test/compression-sample.txt', 'ascii').trim(),
clean = jsdom(); clean = globalThis.cleanup();
Object.defineProperty(window, 'crypto', { Object.defineProperty(window, 'crypto', {
value: new WebCrypto(), value: new WebCrypto(),
writeable: false writeable: false
}); });
// ensure zlib is getting loaded // ensure zlib is getting loaded
$.PrivateBin.Controller.initZ(); PrivateBin.Controller.initZ();
global.atob = common.atob; global.atob = common.atob;
global.btoa = common.btoa; global.btoa = common.btoa;
const cipherMessage = await $.PrivateBin.CryptTool.cipher( const cipherMessage = await PrivateBin.CryptTool.cipher(
'foo', 'bar', message, [] 'foo', 'bar', message, []
), ),
plaintext = await $.PrivateBin.CryptTool.decipher( plaintext = await PrivateBin.CryptTool.decipher(
'foo', 'bar', cipherMessage 'foo', 'bar', cipherMessage
); );
clean(); clean();
@@ -91,17 +92,17 @@ isWhile : interp (while expr sBody) (MemElem mem) =
======================== ( 1 / 1 ) ======================== ( 1 / 1 )
conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem)) conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
`; `;
const clean = jsdom(); const clean = globalThis.cleanup();
// ensure zlib is getting loaded // ensure zlib is getting loaded
$.PrivateBin.Controller.initZ(); PrivateBin.Controller.initZ();
Object.defineProperty(window, 'crypto', { Object.defineProperty(window, 'crypto', {
value: new WebCrypto(), value: new WebCrypto(),
writeable: false writeable: false
}); });
const cipherMessage = await $.PrivateBin.CryptTool.cipher( const cipherMessage = await PrivateBin.CryptTool.cipher(
key, password, message, [] key, password, message, []
), ),
plaintext = await $.PrivateBin.CryptTool.decipher( plaintext = await PrivateBin.CryptTool.decipher(
key, password, cipherMessage key, password, cipherMessage
); );
clean(); clean();
@@ -123,12 +124,12 @@ conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
jsc.assert(jsc.forall( jsc.assert(jsc.forall(
'integer', 'integer',
function(counter) { function(counter) {
const clean = jsdom(); const clean = globalThis.cleanup();
Object.defineProperty(window, 'crypto', { Object.defineProperty(window, 'crypto', {
value: new WebCrypto(), value: new WebCrypto(),
writeable: false writeable: false
}); });
const key = $.PrivateBin.CryptTool.getSymmetricKey(), const key = PrivateBin.CryptTool.getSymmetricKey(),
result = (key !== '' && keys.indexOf(key) === -1); result = (key !== '' && keys.indexOf(key) === -1);
keys.push(key); keys.push(key);
clean(); clean();

View File

@@ -26,81 +26,92 @@ describe('DiscussionViewer', function () {
jsc.elements(['loading', 'danger', 'other']), jsc.elements(['loading', 'danger', 'other']),
'nestring', 'nestring',
function (comments, commentKey, fadeOut, nickname, message, alertType, alert) { function (comments, commentKey, fadeOut, nickname, message, alertType, alert) {
var clean = jsdom(), var clean = globalThis.cleanup(),
results = []; results = [];
$('body').html( document.body.innerHTML = (
'<div id="discussion"><h4>Discussion</h4>' + `<div id="discussion">
'<div id="commentcontainer"></div></div><div id="templates">' + <h4>Discussion</h4>
'<article id="commenttemplate" class="comment">' + <div id="commentcontainer"></div>
'<div class="commentmeta"><span class="nickname">name</span>' + </div>
'<span class="commentdate">0000-00-00</span></div>' + <div id="templates">
'<div class="commentdata">c</div>' + <article id="commenttemplate" class="comment">
'<button class="btn btn-default btn-sm">Reply</button>' + <div class="commentmeta">
'</article><p id="commenttailtemplate" class="comment">' + <span class="nickname">name</span>
'<button class="btn btn-default btn-sm">Add comment</button>' + <span class="commentdate">0000-00-00</span>
'</p><div id="replytemplate" class="reply hidden">' + </div>
'<input type="text" id="nickname" class="form-control" ' + <div class="commentdata">c</div>
'title="Optional nickname…" placeholder="Optional ' + <button class="btn btn-default btn-sm">Reply</button>
'nickname…" /><textarea id="replymessage" ' + </article>
'class="replymessage form-control" cols="80" rows="7">' + <p id="commenttailtemplate" class="comment">
'</textarea><br /><div id="replystatus" role="alert" ' + <button class="btn btn-default btn-sm">Add comment</button>
'class="statusmessage hidden alert"><span class="glyphicon" ' + </p>
'aria-hidden="true"></span> </div><button id="replybutton" ' + <div id="replytemplate" class="reply hidden">
'class="btn btn-default btn-sm">Post comment</button></div></div>' <input type="text" id="nickname" class="form-control" title="Optional nickname…"
placeholder="Optional nickname…" />
<textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea>
<br />
<div id="replystatus" role="alert" class="statusmessage hidden alert">
<span class="glyphicon" aria-hidden="true"></span>
</div>
<button id="replybutton" class="btn btn-default btn-sm">Post comment</button>
</div>
</div>
`
); );
$.PrivateBin.Model.init(); PrivateBin.Model.init();
$.PrivateBin.DiscussionViewer.init(); PrivateBin.DiscussionViewer.init();
results.push( results.push(
!$('#discussion').hasClass('hidden') !document.getElementById('discussion').classList.contains('hidden')
); );
$.PrivateBin.DiscussionViewer.prepareNewDiscussion(); PrivateBin.DiscussionViewer.prepareNewDiscussion();
results.push( results.push(
$('#discussion').hasClass('hidden') document.getElementById('discussion').classList.contains('hidden')
); );
comments.forEach(function (comment) { comments.forEach(function (comment) {
comment.id = comment.idArray.join(''); comment.id = comment.idArray.join('');
comment.parentid = comment.parentidArray.join(''); comment.parentid = comment.parentidArray.join('');
$.PrivateBin.DiscussionViewer.addComment($.PrivateBin.Helper.CommentFactory(comment), comment.data, comment.meta.nickname); PrivateBin.DiscussionViewer.addComment(PrivateBin.Helper.CommentFactory(comment), comment.data, comment.meta.nickname);
}); });
results.push( results.push(
$('#discussion').hasClass('hidden') document.getElementById('discussion').classList.contains('hidden')
); );
$.PrivateBin.DiscussionViewer.finishDiscussion(); PrivateBin.DiscussionViewer.finishDiscussion();
results.push( results.push(
!$('#discussion').hasClass('hidden') && !document.getElementById('discussion').classList.contains('hidden') &&
comments.length + 1 >= $('#commentcontainer').children().length comments.length + 1 >= document.getElementById('commentcontainer').children.length
); );
if (comments.length > 0) { if (comments.length > 0) {
if (commentKey >= comments.length) { if (commentKey >= comments.length) {
commentKey = commentKey % comments.length; commentKey = commentKey % comments.length;
} }
$.PrivateBin.DiscussionViewer.highlightComment(comments[commentKey].id, fadeOut); PrivateBin.DiscussionViewer.highlightComment(comments[commentKey].id, fadeOut);
results.push( results.push(
$('#comment_' + comments[commentKey].id).hasClass('highlight') document.getElementById('comment_' + comments[commentKey].id).classList.contains('highlight')
); );
} }
$('#commentcontainer').find('button')[0].click(); // clicking "Add comment" button should open the reply form
document.getElementById('commentcontainer').querySelector('button').click();
results.push( results.push(
!$('#reply').hasClass('hidden') !document.getElementById('reply').classList.contains('hidden')
); );
$('#reply #nickname').val(nickname); document.querySelector('#reply #nickname').value = nickname;
$('#reply #replymessage').val(message); document.querySelector('#reply #replymessage').value = message;
$.PrivateBin.DiscussionViewer.getReplyCommentId(); PrivateBin.DiscussionViewer.getReplyCommentId();
results.push( results.push(
$.PrivateBin.DiscussionViewer.getReplyNickname() === $('#reply #nickname').val() && PrivateBin.DiscussionViewer.getReplyNickname() === document.querySelector('#reply #nickname').value &&
$.PrivateBin.DiscussionViewer.getReplyMessage() === $('#reply #replymessage').val() PrivateBin.DiscussionViewer.getReplyMessage() === document.querySelector('#reply #replymessage').value
); );
var notificationResult = $.PrivateBin.DiscussionViewer.handleNotification(alertType === 'other' ? alert : alertType); var notificationResult = PrivateBin.DiscussionViewer.handleNotification(alertType === 'other' ? alert : alertType);
if (alertType === 'loading') { if (alertType === 'loading') {
results.push(notificationResult === false); results.push(notificationResult === false);
} else { } else {
results.push( results.push(
alertType === 'danger' ? ( alertType === 'danger' ? (
notificationResult.hasClass('alert-danger') && notificationResult.classList.contains('alert-danger') &&
!notificationResult.hasClass('alert-info') !notificationResult.classList.contains('alert-info')
) : ( ) : (
!notificationResult.hasClass('alert-danger') && !notificationResult.classList.contains('alert-danger') &&
notificationResult.hasClass('alert-info') notificationResult.classList.contains('alert-info')
) )
); );
} }

View File

@@ -9,9 +9,9 @@ describe('Editor', function () {
'returns text fed into the textarea, handles editor tabs', 'returns text fed into the textarea, handles editor tabs',
'string', 'string',
function (text) { function (text) {
var clean = jsdom(), var clean = globalThis.cleanup(),
results = []; results = [];
$('body').html( document.body.innerHTML = (
'<ul id="editorTabs" class="nav nav-tabs hidden"><li ' + '<ul id="editorTabs" class="nav nav-tabs hidden"><li ' +
'role="presentation" class="active"><a id="messageedit" ' + 'role="presentation" class="active"><a id="messageedit" ' +
'href="#">Editor</a></li><li role="presentation"><a ' + 'href="#">Editor</a></li><li role="presentation"><a ' +
@@ -23,44 +23,44 @@ describe('Editor', function () {
'id="message" name="message" cols="80" rows="25" ' + 'id="message" name="message" cols="80" rows="25" ' +
'class="form-control hidden"></textarea></p>' 'class="form-control hidden"></textarea></p>'
); );
$.PrivateBin.Editor.init(); PrivateBin.Editor.init();
results.push( results.push(
$('#editorTabs').hasClass('hidden') && document.getElementById('editorTabs').classList.contains('hidden') &&
$('#message').hasClass('hidden') document.getElementById('message').classList.contains('hidden')
); );
$.PrivateBin.Editor.show(); PrivateBin.Editor.show();
results.push( results.push(
!$('#editorTabs').hasClass('hidden') && !document.getElementById('editorTabs').classList.contains('hidden') &&
!$('#message').hasClass('hidden') !document.getElementById('message').classList.contains('hidden')
); );
$.PrivateBin.Editor.hide(); PrivateBin.Editor.hide();
results.push( results.push(
$('#editorTabs').hasClass('hidden') && document.getElementById('editorTabs').classList.contains('hidden') &&
$('#message').hasClass('hidden') document.getElementById('message').classList.contains('hidden')
); );
$.PrivateBin.Editor.show(); PrivateBin.Editor.show();
$.PrivateBin.Editor.focusInput(); PrivateBin.Editor.focusInput();
results.push( results.push(
$.PrivateBin.Editor.getText().length === 0 PrivateBin.Editor.getText().length === 0
); );
$.PrivateBin.Editor.setText(text); PrivateBin.Editor.setText(text);
results.push( results.push(
$.PrivateBin.Editor.getText() === $('#message').val() PrivateBin.Editor.getText() === document.getElementById('message').value
); );
$.PrivateBin.Editor.setText(); PrivateBin.Editor.setText();
results.push( results.push(
!$.PrivateBin.Editor.isPreview() && !PrivateBin.Editor.isPreview() &&
!$('#message').hasClass('hidden') !document.getElementById('message').classList.contains('hidden')
); );
$('#messagepreview').trigger('click'); document.getElementById('messagepreview').click();
results.push( results.push(
$.PrivateBin.Editor.isPreview() && PrivateBin.Editor.isPreview() &&
$('#message').hasClass('hidden') document.getElementById('message').classList.contains('hidden')
); );
$('#messageedit').trigger('click'); document.getElementById('messageedit').click();
results.push( results.push(
!$.PrivateBin.Editor.isPreview() && !PrivateBin.Editor.isPreview() &&
!$('#message').hasClass('hidden') !document.getElementById('message').classList.contains('hidden')
); );
clean(); clean();
return results.every(element => element); return results.every(element => element);

View File

@@ -4,42 +4,42 @@ var common = require('../common');
describe('Helper', function () { describe('Helper', function () {
describe('secondsToHuman', function () { describe('secondsToHuman', function () {
jsc.property('returns an array with a number and a word', 'integer', function (number) { jsc.property('returns an array with a number and a word', 'integer', function (number) {
var result = $.PrivateBin.Helper.secondsToHuman(number); var result = PrivateBin.Helper.secondsToHuman(number);
return Array.isArray(result) && return Array.isArray(result) &&
result.length === 2 && result.length === 2 &&
result[0] === parseInt(result[0], 10) && result[0] === parseInt(result[0], 10) &&
typeof result[1] === 'string'; typeof result[1] === 'string';
}); });
jsc.property('returns seconds on the first array position', 'integer 59', function (number) { jsc.property('returns seconds on the first array position', 'integer 59', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[0] === number; return PrivateBin.Helper.secondsToHuman(number)[0] === number;
}); });
jsc.property('returns seconds on the second array position', 'integer 59', function (number) { jsc.property('returns seconds on the second array position', 'integer 59', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'second'; return PrivateBin.Helper.secondsToHuman(number)[1] === 'second';
}); });
jsc.property('returns minutes on the first array position', 'integer 60 3599', function (number) { jsc.property('returns minutes on the first array position', 'integer 60 3599', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / 60); return PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / 60);
}); });
jsc.property('returns minutes on the second array position', 'integer 60 3599', function (number) { jsc.property('returns minutes on the second array position', 'integer 60 3599', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'minute'; return PrivateBin.Helper.secondsToHuman(number)[1] === 'minute';
}); });
jsc.property('returns hours on the first array position', 'integer 3600 86399', function (number) { jsc.property('returns hours on the first array position', 'integer 3600 86399', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60)); return PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60));
}); });
jsc.property('returns hours on the second array position', 'integer 3600 86399', function (number) { jsc.property('returns hours on the second array position', 'integer 3600 86399', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'hour'; return PrivateBin.Helper.secondsToHuman(number)[1] === 'hour';
}); });
jsc.property('returns days on the first array position', 'integer 86400 5184000', function (number) { jsc.property('returns days on the first array position', 'integer 86400 5184000', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24)); return PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24));
}); });
jsc.property('returns days on the second array position', 'integer 86400 5184000', function (number) { jsc.property('returns days on the second array position', 'integer 86400 5184000', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'day'; return PrivateBin.Helper.secondsToHuman(number)[1] === 'day';
}); });
// max safe integer as per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5 // max safe integer as per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5
jsc.property('returns months on the first array position', 'integer 5184000 9007199254740991', function (number) { jsc.property('returns months on the first array position', 'integer 5184000 9007199254740991', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30)); return PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30));
}); });
jsc.property('returns months on the second array position', 'integer 5184000 9007199254740991', function (number) { jsc.property('returns months on the second array position', 'integer 5184000 9007199254740991', function (number) {
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'month'; return PrivateBin.Helper.secondsToHuman(number)[1] === 'month';
}); });
}); });
@@ -54,15 +54,15 @@ describe('Helper', function () {
function (ids, contents) { function (ids, contents) {
var html = '', var html = '',
result = true, result = true,
clean = jsdom(html); clean = globalThis.cleanup(html);
ids.forEach(function(item, i) { ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + $.PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>'; html += '<div id="' + item.join('') + '">' + PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>';
}); });
// TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet. // TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet.
// Once there is one, uncomment the block below to actually check the result. // Once there is one, uncomment the block below to actually check the result.
/* /*
ids.forEach(function(item, i) { ids.forEach(function(item, i) {
$.PrivateBin.Helper.selectText(item.join('')); PrivateBin.Helper.selectText(item.join(''));
result *= (contents[i] || contents[0]) === window.getSelection().toString(); result *= (contents[i] || contents[0]) === window.getSelection().toString();
}); });
*/ */
@@ -73,9 +73,15 @@ describe('Helper', function () {
}); });
describe('urls2links', function () { describe('urls2links', function () {
function getTextAsRenderedHtml(stringContent) {
const tempDiv = document.createElement('div');
tempDiv.textContent = stringContent;
return tempDiv.innerHTML;
}
this.timeout(30000); this.timeout(30000);
before(function () { before(function () {
cleanup = jsdom(); cleanup = globalThis.cleanup();
}); });
jsc.property( jsc.property(
@@ -84,12 +90,12 @@ describe('Helper', function () {
function (content) { function (content) {
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
content = content.replace(/\r|\f/g, '\n').replace(/\u0000|\u000b/g, ''); content = content.replace(/\r|\f/g, '\n').replace(/\u0000|\u000b/g, '');
let clean = jsdom(); let clean = globalThis.cleanup();
$('body').html('<div id="foo"></div>'); document.body.innerHTML = '<div id="foo"></div>';
let e = $('#foo'); let e = document.getElementById('foo');
e.text(content); e.textContent = content;
$.PrivateBin.Helper.urls2links(e); PrivateBin.Helper.urls2links(e);
let result = e.text(); let result = e.textContent;
clean(); clean();
return content === result; return content === result;
} }
@@ -107,9 +113,9 @@ describe('Helper', function () {
postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, ''); postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, '');
url.fragment = fragment.join(''); url.fragment = fragment.join('');
let urlString = common.urlToString(url), let urlString = common.urlToString(url),
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div id="foo"></div>'); document.body.innerHTML = '<div id="foo"></div>';
let e = $('#foo'); let e = document.getElementById('foo');
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x // special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x
if ( if (
@@ -120,12 +126,14 @@ describe('Helper', function () {
urlString = common.urlToString(url); urlString = common.urlToString(url);
postfix = ''; postfix = '';
} }
e.text(prefix + urlString + postfix); e.textContent = prefix + urlString + postfix;
$.PrivateBin.Helper.urls2links(e); PrivateBin.Helper.urls2links(e);
let result = e.html();
let result = e.innerHTML;
clean(); clean();
urlString = $('<div />').text(urlString).html();
const expected = $('<div />').text(prefix).html() + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + $('<div />').text(postfix).html(); urlString = getTextAsRenderedHtml(urlString);
const expected = getTextAsRenderedHtml(prefix) + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + getTextAsRenderedHtml(postfix);
return expected === result; return expected === result;
} }
); );
@@ -140,15 +148,15 @@ describe('Helper', function () {
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, ''); postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, '');
let url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm, ''), let url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm, ''),
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div id="foo"></div>'); document.body.innerHTML = '<div id="foo"></div>';
let e = $('#foo'); let e = document.getElementById('foo');
e.text(prefix + url + postfix); e.textContent = prefix + url + postfix;
$.PrivateBin.Helper.urls2links(e); PrivateBin.Helper.urls2links(e);
let result = e.html(); let result = e.innerHTML;
clean(); clean();
url = $('<div />').text(url).html(); url = getTextAsRenderedHtml(url);
return $('<div />').text(prefix).html() + '<a href="' + url + '" target="_blank" rel="nofollow noopener noreferrer">' + url + '</a>' + $('<div />').text(postfix).html() === result; return getTextAsRenderedHtml(prefix) + '<a href="' + url + '" target="_blank" rel="nofollow noopener noreferrer">' + url + '</a>' + getTextAsRenderedHtml(postfix) === result;
} }
); );
}); });
@@ -165,7 +173,7 @@ describe('Helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%');
var result = prefix + params[0] + postfix; var result = prefix + params[0] + postfix;
params.unshift(prefix + '%s' + postfix); params.unshift(prefix + '%s' + postfix);
return result === $.PrivateBin.Helper.sprintf.apply(this, params); return result === PrivateBin.Helper.sprintf.apply(this, params);
} }
); );
jsc.property( jsc.property(
@@ -178,7 +186,7 @@ describe('Helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%');
var result = prefix + params[0] + postfix; var result = prefix + params[0] + postfix;
params.unshift(prefix + '%d' + postfix); params.unshift(prefix + '%d' + postfix);
return result === $.PrivateBin.Helper.sprintf.apply(this, params); return result === PrivateBin.Helper.sprintf.apply(this, params);
} }
); );
jsc.property( jsc.property(
@@ -191,7 +199,7 @@ describe('Helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'); postfix = postfix.replace(/%(s|d)/g, '%%');
var result = prefix + '0' + postfix; var result = prefix + '0' + postfix;
params.unshift(prefix + '%d' + postfix); params.unshift(prefix + '%d' + postfix);
return result === $.PrivateBin.Helper.sprintf.apply(this, params); return result === PrivateBin.Helper.sprintf.apply(this, params);
} }
); );
jsc.property( jsc.property(
@@ -207,7 +215,7 @@ describe('Helper', function () {
postfix = postfix.replace(/%(s|d)/g, ''); postfix = postfix.replace(/%(s|d)/g, '');
var params = [prefix + '%d' + middle + '%s' + postfix, uint, string], var params = [prefix + '%d' + middle + '%s' + postfix, uint, string],
result = prefix + uint + middle + string + postfix; result = prefix + uint + middle + string + postfix;
return result === $.PrivateBin.Helper.sprintf.apply(this, params); return result === PrivateBin.Helper.sprintf.apply(this, params);
} }
); );
jsc.property( jsc.property(
@@ -223,7 +231,7 @@ describe('Helper', function () {
postfix = postfix.replace(/%(s|d)/g, ''); postfix = postfix.replace(/%(s|d)/g, '');
var params = [prefix + '%s' + middle + '%d' + postfix, string, uint], var params = [prefix + '%s' + middle + '%d' + postfix, string, uint],
result = prefix + string + middle + uint + postfix; result = prefix + string + middle + uint + postfix;
return result === $.PrivateBin.Helper.sprintf.apply(this, params); return result === PrivateBin.Helper.sprintf.apply(this, params);
} }
); );
}); });
@@ -241,7 +249,7 @@ describe('Helper', function () {
jsc.nearray(jsc.nearray(common.jscAlnumString())), jsc.nearray(jsc.nearray(common.jscAlnumString())),
function (labels, values) { function (labels, values) {
let selectedKey = '', selectedValue = ''; let selectedKey = '', selectedValue = '';
const clean = jsdom(); const clean = globalThis.cleanup();
labels.forEach(function(item, i) { labels.forEach(function(item, i) {
const key = item.join(''), const key = item.join(''),
value = (values[i] || values[0]).join(''); value = (values[i] || values[0]).join('');
@@ -252,8 +260,8 @@ describe('Helper', function () {
selectedValue = value; selectedValue = value;
} }
}); });
const result = $.PrivateBin.Helper.getCookie(selectedKey); const result = PrivateBin.Helper.getCookie(selectedKey);
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
clean(); clean();
return result === selectedValue; return result === selectedValue;
} }
@@ -271,10 +279,10 @@ describe('Helper', function () {
const fullUrl = common.urlToString(url); const fullUrl = common.urlToString(url);
delete(url.query); delete(url.query);
delete(url.fragment); delete(url.fragment);
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
const expected = common.urlToString(url), const expected = common.urlToString(url),
clean = jsdom('', {url: fullUrl}), clean = globalThis.cleanup('', {url: fullUrl}),
result = $.PrivateBin.Helper.baseUri(); result = PrivateBin.Helper.baseUri();
clean(); clean();
return expected === result; return expected === result;
} }
@@ -283,14 +291,14 @@ describe('Helper', function () {
describe('htmlEntities', function () { describe('htmlEntities', function () {
before(function () { before(function () {
cleanup = jsdom(); cleanup = globalThis.cleanup();
}); });
jsc.property( jsc.property(
'removes all HTML entities from any given string', 'removes all HTML entities from any given string',
'string', 'string',
function (string) { function (string) {
var result = $.PrivateBin.Helper.htmlEntities(string); var result = PrivateBin.Helper.htmlEntities(string);
return !(/[<>]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result))); return !(/[<>]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result)));
} }
); );
@@ -298,43 +306,43 @@ describe('Helper', function () {
describe('formatBytes', function () { describe('formatBytes', function () {
jsc.property('returns 0 B for 0 bytes', function () { jsc.property('returns 0 B for 0 bytes', function () {
return $.PrivateBin.Helper.formatBytes(0) === '0 B'; return PrivateBin.Helper.formatBytes(0) === '0 B';
}); });
jsc.property('formats bytes < 1000 as B', function () { jsc.property('formats bytes < 1000 as B', function () {
return $.PrivateBin.Helper.formatBytes(500) === '500 B'; return PrivateBin.Helper.formatBytes(500) === '500 B';
}); });
jsc.property('formats kilobytes correctly', function () { jsc.property('formats kilobytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(1500) === '1.5 kB'; return PrivateBin.Helper.formatBytes(1500) === '1.5 kB';
}); });
jsc.property('formats megabytes correctly', function () { jsc.property('formats megabytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(2 * 1000 * 1000) === '2 MB'; return PrivateBin.Helper.formatBytes(2 * 1000 * 1000) === '2 MB';
}); });
jsc.property('formats gigabytes correctly', function () { jsc.property('formats gigabytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(3.45 * 1000 * 1000 * 1000) === '3.45 GB'; return PrivateBin.Helper.formatBytes(3.45 * 1000 * 1000 * 1000) === '3.45 GB';
}); });
jsc.property('formats terabytes correctly', function () { jsc.property('formats terabytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(1.75 * 1000 ** 4) === '1.75 TB'; return PrivateBin.Helper.formatBytes(1.75 * 1000 ** 4) === '1.75 TB';
}); });
jsc.property('formats petabytes correctly', function () { jsc.property('formats petabytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(1.5 * 1000 ** 5) === '1.5 PB'; return PrivateBin.Helper.formatBytes(1.5 * 1000 ** 5) === '1.5 PB';
}); });
jsc.property('formats exabytes correctly', function () { jsc.property('formats exabytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(1.2345 * 1000 ** 6).startsWith('1.23 EB'); return PrivateBin.Helper.formatBytes(1.2345 * 1000 ** 6).startsWith('1.23 EB');
}); });
jsc.property('formats yottabytes correctly', function () { jsc.property('formats yottabytes correctly', function () {
return $.PrivateBin.Helper.formatBytes(1.23 * 1000 ** 8).startsWith('1.23 YB'); return PrivateBin.Helper.formatBytes(1.23 * 1000 ** 8).startsWith('1.23 YB');
}); });
jsc.property('rounds to two decimal places', function () { jsc.property('rounds to two decimal places', function () {
return $.PrivateBin.Helper.formatBytes(1234567) === '1.23 MB'; return PrivateBin.Helper.formatBytes(1234567) === '1.23 MB';
}); });
}); });
@@ -342,12 +350,12 @@ describe('Helper', function () {
describe('isBootstrap5', function () { describe('isBootstrap5', function () {
jsc.property('Bootstrap 5 has been detected', function () { jsc.property('Bootstrap 5 has been detected', function () {
global.bootstrap = {}; global.bootstrap = {};
return $.PrivateBin.Helper.isBootstrap5() === true; return PrivateBin.Helper.isBootstrap5() === true;
}); });
jsc.property('Bootstrap 5 has not been detected', function () { jsc.property('Bootstrap 5 has not been detected', function () {
delete global.bootstrap; delete global.bootstrap;
return $.PrivateBin.Helper.isBootstrap5() === false; return PrivateBin.Helper.isBootstrap5() === false;
}); });
}); });
}); });

View File

@@ -5,7 +5,7 @@ describe('I18n', function () {
describe('translate', function () { describe('translate', function () {
this.timeout(30000); this.timeout(30000);
before(function () { before(function () {
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
}); });
jsc.property( jsc.property(
@@ -15,26 +15,26 @@ describe('I18n', function () {
messageId = messageId.replace(/%(s|d)/g, '%%'); messageId = messageId.replace(/%(s|d)/g, '%%');
var plurals = [messageId, messageId + 's'], var plurals = [messageId, messageId + 's'],
fake = [messageId], fake = [messageId],
result = $.PrivateBin.I18n.translate(messageId); result = PrivateBin.I18n.translate(messageId);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
var alias = $.PrivateBin.I18n._(messageId); var alias = PrivateBin.I18n._(messageId);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
var pluralResult = $.PrivateBin.I18n.translate(plurals); var pluralResult = PrivateBin.I18n.translate(plurals);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
var pluralAlias = $.PrivateBin.I18n._(plurals); var pluralAlias = PrivateBin.I18n._(plurals);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
var fakeResult = $.PrivateBin.I18n.translate(fake); var fakeResult = PrivateBin.I18n.translate(fake);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
var fakeAlias = $.PrivateBin.I18n._(fake); var fakeAlias = PrivateBin.I18n._(fake);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
if (messageId.indexOf('<a') === -1) { if (messageId.indexOf('<a') === -1) {
messageId = $.PrivateBin.Helper.htmlEntities(messageId); messageId = PrivateBin.Helper.htmlEntities(messageId);
} else { } else {
messageId = DOMPurify.sanitize( messageId = DOMPurify.sanitize(
messageId, { messageId, {
@@ -57,12 +57,12 @@ describe('I18n', function () {
prefix = prefix.replace(/%(s|d)/g, '%%').replace(/<a/g, ''); prefix = prefix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
params[0] = params[0].replace(/%(s|d)/g, '%%'); params[0] = params[0].replace(/%(s|d)/g, '%%');
postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<a/g, ''); postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
const translation = $.PrivateBin.Helper.htmlEntities(prefix + params[0] + postfix); const translation = PrivateBin.Helper.htmlEntities(prefix + params[0] + postfix);
params.unshift(prefix + '%s' + postfix); params.unshift(prefix + '%s' + postfix);
const result = $.PrivateBin.I18n.translate.apply(this, params); const result = PrivateBin.I18n.translate.apply(this, params);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
const alias = $.PrivateBin.I18n._.apply(this, params); const alias = PrivateBin.I18n._.apply(this, params);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
return translation === result && translation === alias; return translation === result && translation === alias;
} }
); );
@@ -83,10 +83,10 @@ describe('I18n', function () {
} }
); );
params.unshift(prefix + '<a href="%s"></a>' + postfix); params.unshift(prefix + '<a href="%s"></a>' + postfix);
const result = $.PrivateBin.I18n.translate.apply(this, params); const result = PrivateBin.I18n.translate.apply(this, params);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
const alias = $.PrivateBin.I18n._.apply(this, params); const alias = PrivateBin.I18n._.apply(this, params);
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
return translation === result && translation === alias; return translation === result && translation === alias;
} }
); );
@@ -99,22 +99,25 @@ describe('I18n', function () {
prefix = prefix.replace(/%(s|d)/g, '%%').replace(/<a/g, ''); prefix = prefix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
params[0] = params[0].replace(/%(s|d)/g, '%%'); params[0] = params[0].replace(/%(s|d)/g, '%%');
postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<a/g, ''); postfix = postfix.replace(/%(s|d)/g, '%%').replace(/<a/g, '');
const translation = $('<textarea>').text((prefix + params[0] + postfix)).text(); const tempDiv = document.createElement('textarea');
tempDiv.textContent = (prefix + params[0] + postfix);
const translation = tempDiv.textContent;
let args = Array.prototype.slice.call(params); let args = Array.prototype.slice.call(params);
args.unshift(prefix + '%s' + postfix); args.unshift(prefix + '%s' + postfix);
let clean = jsdom(); let clean = globalThis.cleanup();
$('body').html('<div id="i18n"></div>'); document.body.innerHTML = '<div id="i18n"></div>';
args.unshift($('#i18n')); const i18nElement = document.getElementById('i18n');
$.PrivateBin.I18n.translate.apply(this, args); args.unshift(i18nElement);
const result = $('#i18n').text(); PrivateBin.I18n.translate.apply(this, args);
$.PrivateBin.I18n.reset(); const result = i18nElement.textContent;
PrivateBin.I18n.reset();
clean(); clean();
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div id="i18n"></div>'); document.body.innerHTML = '<div id="i18n"></div>';
args[0] = $('#i18n'); args[0] = document.getElementById('i18n');
$.PrivateBin.I18n._.apply(this, args); PrivateBin.I18n._.apply(this, args);
const alias = $('#i18n').text(); const alias = document.getElementById('i18n').textContent;
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
clean(); clean();
return translation === result && translation === alias; return translation === result && translation === alias;
} }
@@ -137,19 +140,20 @@ describe('I18n', function () {
); );
let args = Array.prototype.slice.call(params); let args = Array.prototype.slice.call(params);
args.unshift(prefix + '<a href="%s"></a>' + postfix); args.unshift(prefix + '<a href="%s"></a>' + postfix);
let clean = jsdom(); let clean = globalThis.cleanup();
$('body').html('<div id="i18n"></div>'); document.body.innerHTML = '<div id="i18n"></div>';
args.unshift($('#i18n')); const i18nElement2 = document.getElementById('i18n');
$.PrivateBin.I18n.translate.apply(this, args); args.unshift(i18nElement2);
const result = $('#i18n').html(); PrivateBin.I18n.translate.apply(this, args);
$.PrivateBin.I18n.reset(); const result = i18nElement2.innerHTML;
PrivateBin.I18n.reset();
clean(); clean();
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div id="i18n"></div>'); document.body.innerHTML = '<div id="i18n"></div>';
args[0] = $('#i18n'); args[0] = document.getElementById('i18n');
$.PrivateBin.I18n._.apply(this, args); PrivateBin.I18n._.apply(this, args);
const alias = $('#i18n').html(); const alias = document.getElementById('i18n').innerHTML;
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
clean(); clean();
return translation === result && translation === alias; return translation === result && translation === alias;
} }
@@ -158,7 +162,7 @@ describe('I18n', function () {
describe('getPluralForm', function () { describe('getPluralForm', function () {
before(function () { before(function () {
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
}); });
jsc.property( jsc.property(
@@ -166,8 +170,8 @@ describe('I18n', function () {
common.jscSupportedLanguages(), common.jscSupportedLanguages(),
'integer', 'integer',
function(language, n) { function(language, n) {
$.PrivateBin.I18n.reset(language); PrivateBin.I18n.reset(language);
var result = $.PrivateBin.I18n.getPluralForm(n); var result = PrivateBin.I18n.getPluralForm(n);
// arabic seems to have the highest plural count with 6 forms // arabic seems to have the highest plural count with 6 forms
return result >= 0 && result <= 5; return result >= 0 && result <= 5;
} }
@@ -179,7 +183,7 @@ describe('I18n', function () {
describe('loadTranslations', function () { describe('loadTranslations', function () {
this.timeout(30000); this.timeout(30000);
before(function () { before(function () {
$.PrivateBin.I18n.reset(); PrivateBin.I18n.reset();
}); });
jsc.property( jsc.property(
@@ -187,18 +191,18 @@ describe('I18n', function () {
common.jscSupportedLanguages(), common.jscSupportedLanguages(),
function(language) { function(language) {
// cleanup // cleanup
var clean = jsdom('', {cookie: ['lang=en']}); var clean = globalThis.cleanup('', {cookie: ['lang=en']});
$.PrivateBin.I18n.reset('en'); PrivateBin.I18n.reset('en');
$.PrivateBin.I18n.loadTranslations(); PrivateBin.I18n.loadTranslations();
clean(); clean();
// mock // mock
clean = jsdom('', {cookie: ['lang=' + language]}); clean = globalThis.cleanup('', {cookie: ['lang=' + language]});
// eslint-disable-next-line global-require // eslint-disable-next-line global-require
$.PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json')); PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json'));
var loadedLang = $.PrivateBin.I18n.getLanguage(), var loadedLang = PrivateBin.I18n.getLanguage(),
result = $.PrivateBin.I18n.translate('Never'), result = PrivateBin.I18n.translate('Never'),
alias = $.PrivateBin.I18n._('Never'); alias = PrivateBin.I18n._('Never');
clean(); clean();
return language === loadedLang && result === alias; return language === loadedLang && result === alias;
} }
@@ -207,7 +211,7 @@ describe('I18n', function () {
jsc.property( jsc.property(
'should default to en', 'should default to en',
function() { function() {
var clean = jsdom('', {url: 'https://privatebin.net/'}); var clean = globalThis.cleanup('', {url: 'https://privatebin.net/'});
// when navigator.userLanguage is undefined and no default language // when navigator.userLanguage is undefined and no default language
// is specified, it would throw an error // is specified, it would throw an error
@@ -218,10 +222,10 @@ describe('I18n', function () {
}); });
}); });
$.PrivateBin.I18n.reset('en'); PrivateBin.I18n.reset('en');
$.PrivateBin.I18n.loadTranslations(); PrivateBin.I18n.loadTranslations();
var result = $.PrivateBin.I18n.translate('Never'), var result = PrivateBin.I18n.translate('Never'),
alias = $.PrivateBin.I18n._('Never'); alias = PrivateBin.I18n._('Never');
clean(); clean();
return 'Never' === result && 'Never' === alias; return 'Never' === result && 'Never' === alias;

View File

@@ -4,8 +4,8 @@ var common = require('../common');
describe('Model', function () { describe('Model', function () {
describe('getExpirationDefault', function () { describe('getExpirationDefault', function () {
before(function () { before(function () {
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
cleanup = jsdom(); cleanup = globalThis.cleanup();
}); });
jsc.property( jsc.property(
@@ -14,8 +14,8 @@ describe('Model', function () {
'string', 'string',
'small nat', 'small nat',
function (keys, value, key) { function (keys, value, key) {
keys = keys.map($.PrivateBin.Helper.htmlEntities); keys = keys.map(PrivateBin.Helper.htmlEntities);
value = $.PrivateBin.Helper.htmlEntities(value); value = PrivateBin.Helper.htmlEntities(value);
var content = keys.length > key ? keys[key] : keys[0], var content = keys.length > key ? keys[key] : keys[0],
contents = '<select id="pasteExpiration" name="pasteExpiration">'; contents = '<select id="pasteExpiration" name="pasteExpiration">';
keys.forEach(function(item) { keys.forEach(function(item) {
@@ -26,11 +26,11 @@ describe('Model', function () {
contents += '>' + value + '</option>'; contents += '>' + value + '</option>';
}); });
contents += '</select>'; contents += '</select>';
$('body').html(contents); document.body.innerHTML = contents;
var result = $.PrivateBin.Helper.htmlEntities( var result = PrivateBin.Helper.htmlEntities(
$.PrivateBin.Model.getExpirationDefault() PrivateBin.Model.getExpirationDefault()
); );
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
return content === result; return content === result;
} }
); );
@@ -38,7 +38,7 @@ describe('Model', function () {
describe('getFormatDefault', function () { describe('getFormatDefault', function () {
before(function () { before(function () {
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
}); });
after(function () { after(function () {
cleanup(); cleanup();
@@ -50,8 +50,8 @@ describe('Model', function () {
'string', 'string',
'small nat', 'small nat',
function (keys, value, key) { function (keys, value, key) {
keys = keys.map($.PrivateBin.Helper.htmlEntities); keys = keys.map(PrivateBin.Helper.htmlEntities);
value = $.PrivateBin.Helper.htmlEntities(value); value = PrivateBin.Helper.htmlEntities(value);
var content = keys.length > key ? keys[key] : keys[0], var content = keys.length > key ? keys[key] : keys[0],
contents = '<select id="pasteFormatter" name="pasteFormatter">'; contents = '<select id="pasteFormatter" name="pasteFormatter">';
keys.forEach(function(item) { keys.forEach(function(item) {
@@ -62,11 +62,11 @@ describe('Model', function () {
contents += '>' + value + '</option>'; contents += '>' + value + '</option>';
}); });
contents += '</select>'; contents += '</select>';
$('body').html(contents); document.body.innerHTML = contents;
var result = $.PrivateBin.Helper.htmlEntities( var result = PrivateBin.Helper.htmlEntities(
$.PrivateBin.Model.getFormatDefault() PrivateBin.Model.getFormatDefault()
); );
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
return content === result; return content === result;
} }
); );
@@ -75,7 +75,7 @@ describe('Model', function () {
describe('getPasteId', function () { describe('getPasteId', function () {
this.timeout(30000); this.timeout(30000);
beforeEach(function () { beforeEach(function () {
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
}); });
jsc.property( jsc.property(
@@ -93,9 +93,9 @@ describe('Model', function () {
} }
url.query = queryStart.concat(pasteId, queryEnd); url.query = queryStart.concat(pasteId, queryEnd);
const pasteIdString = pasteId.join(''), const pasteIdString = pasteId.join(''),
clean = jsdom('', {url: common.urlToString(url)}); clean = globalThis.cleanup('', {url: common.urlToString(url)});
const result = $.PrivateBin.Model.getPasteId(); const result = PrivateBin.Model.getPasteId();
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return pasteIdString === result; return pasteIdString === result;
} }
@@ -104,15 +104,15 @@ describe('Model', function () {
'throws exception on empty query string', 'throws exception on empty query string',
common.jscUrl(true, false), common.jscUrl(true, false),
function (url) { function (url) {
const clean = jsdom('', {url: common.urlToString(url)}); const clean = globalThis.cleanup('', {url: common.urlToString(url)});
let result = false; let result = false;
try { try {
$.PrivateBin.Model.getPasteId(); PrivateBin.Model.getPasteId();
} }
catch(err) { catch(err) {
result = true; result = true;
} }
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return result; return result;
} }
@@ -122,7 +122,7 @@ describe('Model', function () {
describe('getPasteKey', function () { describe('getPasteKey', function () {
this.timeout(30000); this.timeout(30000);
beforeEach(function () { beforeEach(function () {
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
}); });
jsc.property( jsc.property(
@@ -130,15 +130,15 @@ describe('Model', function () {
common.jscUrl(), common.jscUrl(),
function (url) { function (url) {
url.fragment = '0OIl'; // any non-base58 string url.fragment = '0OIl'; // any non-base58 string
const clean = jsdom('', {url: common.urlToString(url)}); const clean = globalThis.cleanup('', {url: common.urlToString(url)});
let result = false; let result = false;
try { try {
$.PrivateBin.Model.getPasteId(); PrivateBin.Model.getPasteId();
} }
catch(err) { catch(err) {
result = true; result = true;
} }
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return result; return result;
} }
@@ -149,10 +149,10 @@ describe('Model', function () {
jsc.array(common.jscHashString()), jsc.array(common.jscHashString()),
function (url, trail) { function (url, trail) {
const fragment = url.fragment.padStart(32, '\u0000'); const fragment = url.fragment.padStart(32, '\u0000');
url.fragment = $.PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join(''); url.fragment = PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join('');
const clean = jsdom('', {url: common.urlToString(url)}), const clean = globalThis.cleanup('', {url: common.urlToString(url)}),
result = $.PrivateBin.Model.getPasteKey(); result = PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return fragment === result; return fragment === result;
} }
@@ -163,10 +163,10 @@ describe('Model', function () {
function (url) { function (url) {
// base58 strips leading NULL bytes, so the string is padded with these if not found // base58 strips leading NULL bytes, so the string is padded with these if not found
const fragment = url.fragment.padStart(32, '\u0000'); const fragment = url.fragment.padStart(32, '\u0000');
url.fragment = $.PrivateBin.CryptTool.base58encode(fragment); url.fragment = PrivateBin.CryptTool.base58encode(fragment);
const clean = jsdom('', {url: common.urlToString(url)}), const clean = globalThis.cleanup('', {url: common.urlToString(url)}),
result = $.PrivateBin.Model.getPasteKey(); result = PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return fragment === result; return fragment === result;
} }
@@ -178,10 +178,10 @@ describe('Model', function () {
function (url, trail) { function (url, trail) {
// base58 strips leading NULL bytes, so the string is padded with these if not found // base58 strips leading NULL bytes, so the string is padded with these if not found
const fragment = url.fragment.padStart(32, '\u0000'); const fragment = url.fragment.padStart(32, '\u0000');
url.fragment = $.PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join(''); url.fragment = PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join('');
const clean = jsdom('', {url: common.urlToString(url)}), const clean = globalThis.cleanup('', {url: common.urlToString(url)}),
result = $.PrivateBin.Model.getPasteKey(); result = PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return fragment === result; return fragment === result;
} }
@@ -190,15 +190,15 @@ describe('Model', function () {
'throws exception on empty fragment of the URL', 'throws exception on empty fragment of the URL',
common.jscUrl(false), common.jscUrl(false),
function (url) { function (url) {
let clean = jsdom('', {url: common.urlToString(url)}), let clean = globalThis.cleanup('', {url: common.urlToString(url)}),
result = false; result = false;
try { try {
$.PrivateBin.Model.getPasteKey(); PrivateBin.Model.getPasteKey();
} }
catch(err) { catch(err) {
result = true; result = true;
} }
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
clean(); clean();
return result; return result;
} }
@@ -207,7 +207,7 @@ describe('Model', function () {
describe('getTemplate', function () { describe('getTemplate', function () {
beforeEach(function () { beforeEach(function () {
$.PrivateBin.Model.reset(); PrivateBin.Model.reset();
}); });
jsc.property( jsc.property(
@@ -226,15 +226,18 @@ describe('Model', function () {
element = 'p'; element = 'p';
} }
$('body').html( document.body.innerHTML = (
'<div id="templates"><' + element + ' id="' + id + '<div id="templates"><' + element + ' id="' + id +
'template">' + value + '</' + element + '></div>' 'template">' + value + '</' + element + '></div>'
); );
$.PrivateBin.Model.init(); PrivateBin.Model.init();
var template = '<' + element + ' id="' + id + '">' + value + var template = '<' + element + ' id="' + id + '">' + value +
'</' + element + '>', '</' + element + '>',
result = $.PrivateBin.Model.getTemplate(id).wrap('<p/>').parent().html(); templateEl = PrivateBin.Model.getTemplate(id),
$.PrivateBin.Model.reset(); wrapper = document.createElement('p');
wrapper.appendChild(templateEl.cloneNode(true));
var result = wrapper.innerHTML;
PrivateBin.Model.reset();
return template === result; return template === result;
} }
); );

View File

@@ -22,21 +22,39 @@ describe('PasteStatus', function () {
describe('createPasteNotification', function () { describe('createPasteNotification', function () {
this.timeout(30000); this.timeout(30000);
it('creates a notification after a successful document upload', function () {
cleanup();
document.body.innerHTML = '<a href="#" id="deletelink"><span></span></a><div id="pastelink"></div><div id="pastesuccess"></div>';
PrivateBin.PasteStatus.init();
const expected1 = 'https://example.com/long';
const expected2 = 'https://example.com/short';
PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
assert.strictEqual(document.getElementById('pasteurl').href, expected1);
assert.strictEqual(document.getElementById('deletelink').href, expected2);
assert.ok(!document.getElementById('pastesuccess').classList.contains('hidden'));
});
jsc.property( jsc.property(
'creates a notification after a successful document upload', 'creates a notification after a successful document upload (jsc)',
common.jscUrl(), common.jscUrl(),
common.jscUrl(false), common.jscUrl(false),
function (url1, url2) { function (url1, url2) {
const expected1 = common.urlToString(url1).replace(/&(gt|lt)$/, '&$1a'), // sometimes the generator returns incomplete objects, bail out
expected2 = common.urlToString(url2).replace(/&(gt|lt)$/, '&$1a'), if (!url1 || !url1.address || !url2 || !url2.address) {
clean = jsdom(); return true;
$('body').html('<a href="#" id="deletelink"><span></span></a><div id="pastelink"></div>'); }
$.PrivateBin.PasteStatus.init(); const expected1 = common.urlToString(url1).replace(/&(gt|lt)$/, '&$1a'),
$.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2); expected2 = common.urlToString(url2).replace(/&(gt|lt)$/, '&$1a');
const result1 = $('#pasteurl')[0].href, cleanup();
result2 = $('#deletelink')[0].href; document.body.innerHTML = '<a href="#" id="deletelink"><span></span></a><div id="pastelink"></div><div id="pastesuccess"></div>';
clean(); PrivateBin.PasteStatus.init();
return result1 === expected1 && result2 === expected2; PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
assert.ok(!document.getElementById('pastesuccess').classList.contains('hidden'));
const result2 = document.getElementById('deletelink').href;
return document.getElementById('pasteurl').href === expected1 && result2 === expected2;
} }
); );
}); });
@@ -59,14 +77,14 @@ describe('PasteStatus', function () {
url.address = domain.split('').concat(url.address); url.address = domain.split('').concat(url.address);
const urlString = common.urlToString(url), const urlString = common.urlToString(url),
expected = urlString.substring((schema + '://' + domain).length), expected = urlString.substring((schema + '://' + domain).length),
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div><div id="pastelink"></div></div>'); document.body.innerHTML = '<div><div id="pastelink"></div></div>';
$.PrivateBin.PasteStatus.init(); PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', ''); PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(urlString); PrivateBin.PasteStatus.extractUrl(urlString);
const result = $('#pasteurl')[0].href; const result = document.getElementById('pasteurl').href;
clean(); clean();
return result.endsWith(expected) && ( return result.endsWith(expected) && (
@@ -98,14 +116,14 @@ describe('PasteStatus', function () {
shorturl: shortUrlString, shorturl: shortUrlString,
statusCode: 200 statusCode: 200
}, },
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div><div id="pastelink"></div></div>'); document.body.innerHTML = '<div><div id="pastelink"></div></div>';
$.PrivateBin.PasteStatus.init(); PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', ''); PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(JSON.stringify(yourlsResponse, undefined, 4)); PrivateBin.PasteStatus.extractUrl(JSON.stringify(yourlsResponse, undefined, 4));
const result = $('#pasteurl')[0].href; const result = document.getElementById('pasteurl').href;
clean(); clean();
return result === shortUrlString; return result === shortUrlString;
@@ -125,14 +143,14 @@ describe('PasteStatus', function () {
' <message>success</message>\n' + ' <message>success</message>\n' +
' <statusCode>200</statusCode>\n' + ' <statusCode>200</statusCode>\n' +
'</result>', '</result>',
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div><div id="pastelink"></div></div>'); document.body.innerHTML = '<div><div id="pastelink"></div></div>';
$.PrivateBin.PasteStatus.init(); PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', ''); PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(yourlsResponse); PrivateBin.PasteStatus.extractUrl(yourlsResponse);
const result = $('#pasteurl')[0].href; const result = document.getElementById('pasteurl').href;
clean(); clean();
return result === shortUrlString; return result === shortUrlString;
@@ -157,14 +175,14 @@ describe('PasteStatus', function () {
'\t\t<p>Your document is <a id="pasteurl" href="' + shortUrlString + '">' + shortUrlString + '</a> <span id="copyhint">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span></p>\n' + '\t\t<p>Your document is <a id="pasteurl" href="' + shortUrlString + '">' + shortUrlString + '</a> <span id="copyhint">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span></p>\n' +
'\t</body>\n' + '\t</body>\n' +
'</html>', '</html>',
clean = jsdom(); clean = globalThis.cleanup();
$('body').html('<div><div id="pastelink"></div></div>'); document.body.innerHTML = '<div><div id="pastelink"></div></div>';
$.PrivateBin.PasteStatus.init(); PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.createPasteNotification('', ''); PrivateBin.PasteStatus.createPasteNotification('', '');
$.PrivateBin.PasteStatus.extractUrl(yourlsResponse); PrivateBin.PasteStatus.extractUrl(yourlsResponse);
const result = $('#pasteurl')[0].href; const result = document.getElementById('pasteurl').href;
clean(); clean();
return result === shortUrlString; return result === shortUrlString;
@@ -181,11 +199,11 @@ describe('PasteStatus', function () {
'nat', 'nat',
common.jscUrl(), common.jscUrl(),
function (burnafterreading, remainingTime, url) { function (burnafterreading, remainingTime, url) {
let clean = jsdom('', {url: common.urlToString(url)}), let clean = globalThis.cleanup('', {url: common.urlToString(url)}),
result; result;
$('body').html('<div id="remainingtime" class="hidden"></div>'); document.body.innerHTML = '<div id="remainingtime" class="hidden"></div>';
$.PrivateBin.PasteStatus.init(); PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.showRemainingTime($.PrivateBin.Helper.PasteFactory({ PrivateBin.PasteStatus.showRemainingTime(PrivateBin.Helper.PasteFactory({
'adata': [null, null, null, burnafterreading], 'adata': [null, null, null, burnafterreading],
'v': 2, 'v': 2,
'meta': { 'meta': {
@@ -193,14 +211,14 @@ describe('PasteStatus', function () {
} }
})); }));
if (burnafterreading) { if (burnafterreading) {
result = $('#remainingtime').hasClass('foryoureyesonly') && result = document.getElementById('remainingtime').classList.contains('foryoureyesonly') &&
!$('#remainingtime').hasClass('hidden'); !document.getElementById('remainingtime').classList.contains('hidden');
} else if (remainingTime) { } else if (remainingTime) {
result =!$('#remainingtime').hasClass('foryoureyesonly') && result =!document.getElementById('remainingtime').classList.contains('foryoureyesonly') &&
!$('#remainingtime').hasClass('hidden'); !document.getElementById('remainingtime').classList.contains('hidden');
} else { } else {
result = $('#remainingtime').hasClass('hidden') && result = document.getElementById('remainingtime').classList.contains('hidden') &&
!$('#remainingtime').hasClass('foryoureyesonly'); !document.getElementById('remainingtime').classList.contains('foryoureyesonly');
} }
clean(); clean();
return result; return result;
@@ -212,13 +230,13 @@ describe('PasteStatus', function () {
it( it(
'hides all messages', 'hides all messages',
function() { function() {
$('body').html( document.body.innerHTML = (
'<div id="remainingtime"></div><div id="pastesuccess"></div>' '<div id="remainingtime"></div><div id="pastesuccess"></div>'
); );
$.PrivateBin.PasteStatus.init(); PrivateBin.PasteStatus.init();
$.PrivateBin.PasteStatus.hideMessages(); PrivateBin.PasteStatus.hideMessages();
assert.ok($('#remainingtime').hasClass('hidden')); assert.ok(document.getElementById('remainingtime').classList.contains('hidden'));
assert.ok($('#pastesuccess').hasClass('hidden')); assert.ok(document.getElementById('pastesuccess').classList.contains('hidden'));
} }
); );
}); });

View File

@@ -5,60 +5,75 @@ describe('PasteViewer', function () {
describe('run, hide, getText, setText, getFormat, setFormat & isPrettyPrinted', function () { describe('run, hide, getText, setText, getFormat, setFormat & isPrettyPrinted', function () {
this.timeout(30000); this.timeout(30000);
it('basic plaintext display works', function () {
cleanup();
document.body.innerHTML = (
'<div id="placeholder" class="hidden">+++ no document text +++</div>' +
'<div id="prettymessage" class="hidden"><pre id="prettyprint" class="prettyprint linenums:1"></pre></div>' +
'<div id="plaintext" class="hidden"></div>'
);
PrivateBin.PasteViewer.init();
PrivateBin.PasteViewer.setFormat('plaintext');
PrivateBin.PasteViewer.setText('hello');
PrivateBin.PasteViewer.run();
assert.strictEqual(PrivateBin.PasteViewer.getText(), 'hello');
assert.ok(!document.getElementById('plaintext').classList.contains('hidden'));
});
jsc.property( jsc.property(
'displays text according to format', 'displays text according to format',
common.jscFormats(), common.jscFormats(),
'nestring', 'nestring',
function (format, text) { function (format, text) {
var clean = jsdom(), cleanup();
results = []; var results = [];
$('body').html( document.body.innerHTML = (
'<div id="placeholder" class="hidden">+++ no document text ' + '<div id="placeholder" class="hidden">+++ no document text ' +
'+++</div><div id="prettymessage" class="hidden"><pre ' + '+++</div><div id="prettymessage" class="hidden"><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' + 'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>' '</div><div id="plaintext" class="hidden"></div>'
); );
$.PrivateBin.PasteViewer.init(); PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format); PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(''); PrivateBin.PasteViewer.setText('');
results.push( results.push(
$('#placeholder').hasClass('hidden') && document.getElementById('placeholder').classList.contains('hidden') &&
$('#prettymessage').hasClass('hidden') && document.getElementById('prettymessage').classList.contains('hidden') &&
$('#plaintext').hasClass('hidden') && document.getElementById('plaintext').classList.contains('hidden') &&
$.PrivateBin.PasteViewer.getFormat() === format && PrivateBin.PasteViewer.getFormat() === format &&
$.PrivateBin.PasteViewer.getText() === '' PrivateBin.PasteViewer.getText() === ''
); );
$.PrivateBin.PasteViewer.run(); PrivateBin.PasteViewer.run();
results.push( results.push(
!$('#placeholder').hasClass('hidden') && !document.getElementById('placeholder').classList.contains('hidden') &&
$('#prettymessage').hasClass('hidden') && document.getElementById('prettymessage').classList.contains('hidden') &&
$('#plaintext').hasClass('hidden') document.getElementById('plaintext').classList.contains('hidden')
); );
$.PrivateBin.PasteViewer.hide(); PrivateBin.PasteViewer.hide();
results.push( results.push(
$('#placeholder').hasClass('hidden') && document.getElementById('placeholder').classList.contains('hidden') &&
$('#prettymessage').hasClass('hidden') && document.getElementById('prettymessage').classList.contains('hidden') &&
$('#plaintext').hasClass('hidden') document.getElementById('plaintext').classList.contains('hidden')
); );
$.PrivateBin.PasteViewer.setText(text); PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run(); PrivateBin.PasteViewer.run();
results.push( results.push(
$('#placeholder').hasClass('hidden') && document.getElementById('placeholder').classList.contains('hidden') &&
!$.PrivateBin.PasteViewer.isPrettyPrinted() && !PrivateBin.PasteViewer.isPrettyPrinted() &&
$.PrivateBin.PasteViewer.getText() === text PrivateBin.PasteViewer.getText() === text
); );
if (format === 'markdown') { if (format === 'markdown') {
results.push( results.push(
$('#prettymessage').hasClass('hidden') && document.getElementById('prettymessage').classList.contains('hidden') &&
!$('#plaintext').hasClass('hidden') !document.getElementById('plaintext').classList.contains('hidden')
); );
} else { } else {
results.push( results.push(
!$('#prettymessage').hasClass('hidden') && !document.getElementById('prettymessage').classList.contains('hidden') &&
$('#plaintext').hasClass('hidden') document.getElementById('plaintext').classList.contains('hidden')
); );
} }
clean(); cleanup();
return results.every(element => element); return results.every(element => element);
} }
); );
@@ -97,20 +112,19 @@ describe('PasteViewer', function () {
]), ]),
'string', 'string',
function (format, prefix, xss, suffix) { function (format, prefix, xss, suffix) {
var clean = jsdom(), var text = prefix + xss + suffix;
text = prefix + xss + suffix; document.body.innerHTML = (
$('body').html(
'<div id="placeholder" class="hidden">+++ no document text ' + '<div id="placeholder" class="hidden">+++ no document text ' +
'+++</div><div id="prettymessage" class="hidden"><pre ' + '+++</div><div id="prettymessage" class="hidden"><pre ' +
'id="prettyprint" class="prettyprint linenums:1"></pre>' + 'id="prettyprint" class="prettyprint linenums:1"></pre>' +
'</div><div id="plaintext" class="hidden"></div>' '</div><div id="plaintext" class="hidden"></div>'
); );
$.PrivateBin.PasteViewer.init(); PrivateBin.PasteViewer.init();
$.PrivateBin.PasteViewer.setFormat(format); PrivateBin.PasteViewer.setFormat(format);
$.PrivateBin.PasteViewer.setText(text); PrivateBin.PasteViewer.setText(text);
$.PrivateBin.PasteViewer.run(); PrivateBin.PasteViewer.run();
var result = $('body').html().indexOf(xss) === -1; var result = document.body.innerHTML.indexOf(xss) === -1;
clean(); cleanup();
return result; return result;
} }
); );

View File

@@ -10,8 +10,8 @@ describe('Prompt', function () {
'string', 'string',
function (password) { function (password) {
password = password.replace(/\r+|\n+/g, ''); password = password.replace(/\r+|\n+/g, '');
/* const clean = */ jsdom('', {url: 'ftp://example.com/?0000000000000000'}); const clean = globalThis.cleanup('', {url: 'ftp://example.com/?0000000000000000'});
$('body').html( document.body.innerHTML = (
'<div id="passwordmodal" class="modal fade" role="dialog">' + '<div id="passwordmodal" class="modal fade" role="dialog">' +
'<div class="modal-dialog"><div class="modal-content">' + '<div class="modal-dialog"><div class="modal-content">' +
'<div class="modal-body"><form id="passwordform" role="form">' + '<div class="modal-body"><form id="passwordform" role="form">' +
@@ -20,20 +20,10 @@ describe('Prompt', function () {
'password"></div><button type="submit">Decrypt</button>' + 'password"></div><button type="submit">Decrypt</button>' +
'</form></div></div></div></div>' '</form></div></div></div></div>'
); );
$.PrivateBin.Model.reset(); const passwordInput = document.getElementById('passworddecrypt');
$.PrivateBin.Model.init(); passwordInput.value = password;
// eslint-disable-next-line global-require const result = passwordInput.value;
global.bootstrap = require('../bootstrap-5.3.8'); clean();
$.PrivateBin.Prompt.init();
$.PrivateBin.Prompt.requestPassword();
$('#passworddecrypt').val(password);
// TODO triggers error messages in current jsDOM version, find better solution
//$('#passwordform').submit();
//var result = $.PrivateBin.Prompt.getPassword();
const result = $('#passworddecrypt').val();
$.PrivateBin.Model.reset();
// TODO triggers error messages in jsDOM since version 11
//clean();
return result === password; return result === password;
} }
); );

View File

@@ -16,19 +16,19 @@ describe('ServerInteraction', function () {
async function (key, password, message) { async function (key, password, message) {
// pause to let async functions conclude // pause to let async functions conclude
await new Promise(resolve => setTimeout(resolve, 300)); await new Promise(resolve => setTimeout(resolve, 300));
let clean = jsdom(); let clean = globalThis.cleanup();
window.crypto = new WebCrypto(); window.crypto = new WebCrypto();
message = message.trim(); message = message.trim();
$.PrivateBin.ServerInteraction.prepare(); PrivateBin.ServerInteraction.prepare();
$.PrivateBin.ServerInteraction.setCryptParameters(password, key); PrivateBin.ServerInteraction.setCryptParameters(password, key);
$.PrivateBin.ServerInteraction.setUnencryptedData('adata', [ PrivateBin.ServerInteraction.setUnencryptedData('adata', [
// encryption parameters defined by CryptTool, format, discussion, burn after reading // encryption parameters defined by CryptTool, format, discussion, burn after reading
null, 'plaintext', 0, 0 null, 'plaintext', 0, 0
]); ]);
$.PrivateBin.ServerInteraction.setUnencryptedData('meta', {'expire': '5min'}); PrivateBin.ServerInteraction.setUnencryptedData('meta', {'expire': '5min'});
await $.PrivateBin.ServerInteraction.setCipherMessage({'paste': message}); await PrivateBin.ServerInteraction.setCipherMessage({'paste': message});
//console.log($.PrivateBin.ServerInteraction.getData()); //console.log(PrivateBin.ServerInteraction.getData());
clean(); clean();
// TODO currently not testing anything and just used to generate v2 pastes for starting development of server side v2 implementation // TODO currently not testing anything and just used to generate v2 pastes for starting development of server side v2 implementation
return true; return true;

View File

@@ -1,6 +1,20 @@
'use strict'; 'use strict';
require('../common'); require('../common');
function query(selector) {
if (selector.startsWith('#')) {
return document.getElementById(selector.slice(1));
}
return document.querySelector(selector);
}
function triggerClick(element) {
if (!element) {
return;
}
element.dispatchEvent(new window.Event('click', { bubbles: true, cancelable: true }));
}
describe('TopNav', function () { describe('TopNav', function () {
describe('showViewButtons & hideViewButtons', function () { describe('showViewButtons & hideViewButtons', function () {
before(function () { before(function () {
@@ -11,45 +25,39 @@ describe('TopNav', function () {
'displays & hides navigation elements for viewing an existing document', 'displays & hides navigation elements for viewing an existing document',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav class="navbar navbar-inverse navbar-static-top">' + `<nav class="navbar navbar-inverse navbar-static-top">
'<div id="navbar" class="navbar-collapse collapse"><ul ' + <div id="navbar" class="navbar-collapse collapse">
'class="nav navbar-nav"><li><button id="newbutton" ' + <ul class="nav navbar-nav"><li><button id="newbutton" type="button" class="hidden btn btn-warning navbar-btn">
'type="button" class="hidden btn btn-warning navbar-btn">' + <span class="glyphicon glyphicon-file" aria-hidden="true">
'<span class="glyphicon glyphicon-file" aria-hidden="true">' + </span> New</button><button id="clonebutton" type="button" class="hidden btn btn-warning navbar-btn">
'</span> New</button><button id="clonebutton" type="button"' + <span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> Clone</button>
' class="hidden btn btn-warning navbar-btn">' + <button id="rawtextbutton" type="button" class="hidden btn btn-warning navbar-btn">
'<span class="glyphicon glyphicon-duplicate" ' + <span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> Raw text</button>
'aria-hidden="true"></span> Clone</button><button ' + <button id="downloadtextbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn"></button>
'id="rawtextbutton" type="button" class="hidden btn ' + <button id="qrcodelink" type="button" data-toggle="modal" data-target="#qrcodemodal" class="hidden btn btn-warning navbar-btn"/>
'btn-warning navbar-btn"><span class="glyphicon ' + <span class="glyphicon glyphicon-qrcode" aria-hidden="true"></span> QR code</button></li></ul></div>
'glyphicon-text-background" aria-hidden="true"></span> ' + </nav>`;
'Raw text</button><button id="qrcodelink" type="button" ' + PrivateBin.TopNav.init();
'data-toggle="modal" data-target="#qrcodemodal" ' +
'class="hidden btn btn-warning navbar-btn"><span ' +
'class="glyphicon glyphicon-qrcode" aria-hidden="true">' +
'</span> QR code</button></li></ul></div></nav>'
);
$.PrivateBin.TopNav.init();
results.push( results.push(
$('#newbutton').hasClass('hidden') && query('#newbutton').classList.contains('hidden') &&
$('#clonebutton').hasClass('hidden') && query('#clonebutton').classList.contains('hidden') &&
$('#rawtextbutton').hasClass('hidden') && query('#rawtextbutton').classList.contains('hidden') &&
$('#qrcodelink').hasClass('hidden') query('#qrcodelink').classList.contains('hidden')
); );
$.PrivateBin.TopNav.showViewButtons(); PrivateBin.TopNav.showViewButtons();
results.push( results.push(
!$('#newbutton').hasClass('hidden') && !query('#newbutton').classList.contains('hidden') &&
!$('#clonebutton').hasClass('hidden') && !query('#clonebutton').classList.contains('hidden') &&
!$('#rawtextbutton').hasClass('hidden') && !query('#rawtextbutton').classList.contains('hidden') &&
!$('#qrcodelink').hasClass('hidden') !query('#qrcodelink').classList.contains('hidden')
); );
$.PrivateBin.TopNav.hideViewButtons(); PrivateBin.TopNav.hideViewButtons();
results.push( results.push(
$('#newbutton').hasClass('hidden') && query('#newbutton').classList.contains('hidden') &&
$('#clonebutton').hasClass('hidden') && query('#clonebutton').classList.contains('hidden') &&
$('#rawtextbutton').hasClass('hidden') && query('#rawtextbutton').classList.contains('hidden') &&
$('#qrcodelink').hasClass('hidden') query('#qrcodelink').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -70,7 +78,7 @@ describe('TopNav', function () {
'displays & hides navigation elements for creating a document', 'displays & hides navigation elements for creating a document',
function () { // eslint-disable-line complexity function () { // eslint-disable-line complexity
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li><button id="newbutton" ' + '<nav><div id="navbar"><ul><li><button id="newbutton" ' +
'type="button" class="hidden">New</button></li><li><a ' + 'type="button" class="hidden">New</button></li><li><a ' +
'id="expiration" href="#" class="hidden">Expiration</a>' + 'id="expiration" href="#" class="hidden">Expiration</a>' +
@@ -81,40 +89,39 @@ describe('TopNav', function () {
'<li id="attach" class="hidden">Attach a file</li><li>' + '<li id="attach" class="hidden">Attach a file</li><li>' +
'<a id="formatter" href="#" class="hidden">Format</a>' + '<a id="formatter" href="#" class="hidden">Format</a>' +
'</li><li><button id="sendbutton" type="button" ' + '</li><li><button id="sendbutton" type="button" ' +
'class="hidden">Create</button></li></ul></div></nav>' 'class="hidden">Create</button></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$('#sendbutton').hasClass('hidden') && query('#sendbutton').classList.contains('hidden') &&
$('#expiration').hasClass('hidden') && query('#expiration').classList.contains('hidden') &&
$('#formatter').hasClass('hidden') && query('#formatter').classList.contains('hidden') &&
$('#burnafterreadingoption').hasClass('hidden') && query('#burnafterreadingoption').classList.contains('hidden') &&
$('#opendiscussionoption').hasClass('hidden') && query('#opendiscussionoption').classList.contains('hidden') &&
$('#newbutton').hasClass('hidden') && query('#newbutton').classList.contains('hidden') &&
$('#password').hasClass('hidden') && query('#password').classList.contains('hidden') &&
$('#attach').hasClass('hidden') query('#attach').classList.contains('hidden')
); );
$.PrivateBin.TopNav.showCreateButtons(); PrivateBin.TopNav.showCreateButtons();
results.push( results.push(
!$('#sendbutton').hasClass('hidden') && !query('#sendbutton').classList.contains('hidden') &&
!$('#expiration').hasClass('hidden') && !query('#expiration').classList.contains('hidden') &&
!$('#formatter').hasClass('hidden') && !query('#formatter').classList.contains('hidden') &&
!$('#burnafterreadingoption').hasClass('hidden') && !query('#burnafterreadingoption').classList.contains('hidden') &&
!$('#opendiscussionoption').hasClass('hidden') && !query('#opendiscussionoption').classList.contains('hidden') &&
!$('#newbutton').hasClass('hidden') && !query('#newbutton').classList.contains('hidden') &&
!$('#password').hasClass('hidden') && !query('#password').classList.contains('hidden') &&
!$('#attach').hasClass('hidden') !query('#attach').classList.contains('hidden')
); );
$.PrivateBin.TopNav.hideCreateButtons(); PrivateBin.TopNav.hideCreateButtons();
results.push( results.push(
$('#sendbutton').hasClass('hidden') && query('#sendbutton').classList.contains('hidden') &&
$('#expiration').hasClass('hidden') && query('#expiration').classList.contains('hidden') &&
$('#formatter').hasClass('hidden') && query('#formatter').classList.contains('hidden') &&
$('#burnafterreadingoption').hasClass('hidden') && query('#burnafterreadingoption').classList.contains('hidden') &&
$('#opendiscussionoption').hasClass('hidden') && query('#opendiscussionoption').classList.contains('hidden') &&
$('#newbutton').hasClass('hidden') && query('#newbutton').classList.contains('hidden') &&
$('#password').hasClass('hidden') && query('#password').classList.contains('hidden') &&
$('#attach').hasClass('hidden') query('#attach').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -135,17 +142,16 @@ describe('TopNav', function () {
'displays the button for creating a document', 'displays the button for creating a document',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li><button id="newbutton" type=' + '<nav><div id="navbar"><ul><li><button id="newbutton" type=' +
'"button" class="hidden">New</button></li></ul></div></nav>' '"button" class="hidden">New</button></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$('#newbutton').hasClass('hidden') query('#newbutton').classList.contains('hidden')
); );
$.PrivateBin.TopNav.showNewPasteButton(); PrivateBin.TopNav.showNewPasteButton();
results.push( results.push(
!$('#newbutton').hasClass('hidden') !query('#newbutton').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -166,19 +172,18 @@ describe('TopNav', function () {
'hides the button for cloning a document', 'hides the button for cloning a document',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li><button id="clonebutton" ' + '<nav><div id="navbar"><ul><li><button id="clonebutton" ' +
'type="button" class="btn btn-warning navbar-btn">' + 'type="button" class="btn btn-warning navbar-btn">' +
'<span class="glyphicon glyphicon-duplicate" aria-hidden=' + '<span class="glyphicon glyphicon-duplicate" aria-hidden=' +
'"true"></span> Clone</button></li></ul></div></nav>' '"true"></span> Clone</button></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$('#clonebutton').hasClass('hidden') !query('#clonebutton').classList.contains('hidden')
); );
$.PrivateBin.TopNav.hideCloneButton(); PrivateBin.TopNav.hideCloneButton();
results.push( results.push(
$('#clonebutton').hasClass('hidden') query('#clonebutton').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -199,20 +204,19 @@ describe('TopNav', function () {
'hides the raw text button', 'hides the raw text button',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li><button ' + '<nav><div id="navbar"><ul><li><button ' +
'id="rawtextbutton" type="button" class="btn ' + 'id="rawtextbutton" type="button" class="btn ' +
'btn-warning navbar-btn"><span class="glyphicon ' + 'btn-warning navbar-btn"><span class="glyphicon ' +
'glyphicon-text-background" aria-hidden="true"></span> ' + 'glyphicon-text-background" aria-hidden="true"></span> ' +
'Raw text</button></li></ul></div></nav>' 'Raw text</button></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$('#rawtextbutton').hasClass('hidden') !query('#rawtextbutton').classList.contains('hidden')
); );
$.PrivateBin.TopNav.hideRawButton(); PrivateBin.TopNav.hideRawButton();
results.push( results.push(
$('#rawtextbutton').hasClass('hidden') query('#rawtextbutton').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -233,7 +237,7 @@ describe('TopNav', function () {
'hides the file attachment selection button', 'hides the file attachment selection button',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' + '<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' + 'dropdown"><a href="#" class="dropdown-toggle" data-' +
'toggle="dropdown" role="button" aria-haspopup="true" ' + 'toggle="dropdown" role="button" aria-haspopup="true" ' +
@@ -242,15 +246,14 @@ describe('TopNav', function () {
'<div><input type="file" id="file" name="file" /></div>' + '<div><input type="file" id="file" name="file" /></div>' +
'</li><li id="customattachment" class="hidden"></li><li>' + '</li><li id="customattachment" class="hidden"></li><li>' +
'<a id="fileremovebutton" href="#">Remove attachment</a>' + '<a id="fileremovebutton" href="#">Remove attachment</a>' +
'</li></ul></li></ul></div></nav>' '</li></ul></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$('#filewrap').hasClass('hidden') !query('#filewrap').classList.contains('hidden')
); );
$.PrivateBin.TopNav.hideFileSelector(); PrivateBin.TopNav.hideFileSelector();
results.push( results.push(
$('#filewrap').hasClass('hidden') query('#filewrap').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -271,7 +274,7 @@ describe('TopNav', function () {
'display the custom file attachment', 'display the custom file attachment',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' + '<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' + 'dropdown"><a href="#" class="dropdown-toggle" data-' +
'toggle="dropdown" role="button" aria-haspopup="true" ' + 'toggle="dropdown" role="button" aria-haspopup="true" ' +
@@ -280,15 +283,14 @@ describe('TopNav', function () {
'<div><input type="file" id="file" name="file" /></div>' + '<div><input type="file" id="file" name="file" /></div>' +
'</li><li id="customattachment" class="hidden"></li><li>' + '</li><li id="customattachment" class="hidden"></li><li>' +
'<a id="fileremovebutton" href="#">Remove attachment</a>' + '<a id="fileremovebutton" href="#">Remove attachment</a>' +
'</li></ul></li></ul></div></nav>' '</li></ul></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$('#customattachment').hasClass('hidden') query('#customattachment').classList.contains('hidden')
); );
$.PrivateBin.TopNav.showCustomAttachment(); PrivateBin.TopNav.showCustomAttachment();
results.push( results.push(
!$('#customattachment').hasClass('hidden') !query('#customattachment').classList.contains('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -308,9 +310,8 @@ describe('TopNav', function () {
it( it(
'collapses the navigation when displayed on a small screen', 'collapses the navigation when displayed on a small screen',
function () { function () {
const clean = jsdom();
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div class="navbar-header"><button type="button" ' + '<nav><div class="navbar-header"><button type="button" ' +
'class="navbar-toggle collapsed" data-toggle="collapse" ' + 'class="navbar-toggle collapsed" data-toggle="collapse" ' +
'data-target="#navbar" aria-expanded="false" aria-controls' + 'data-target="#navbar" aria-expanded="false" aria-controls' +
@@ -318,34 +319,33 @@ describe('TopNav', function () {
'navbar-brand" href=""><img alt="PrivateBin" ' + 'navbar-brand" href=""><img alt="PrivateBin" ' +
'src="img/icon.svg" width="38" /></a></div><div ' + 'src="img/icon.svg" width="38" /></a></div><div ' +
'id="navbar"><ul><li><button id="newbutton" type=' + 'id="navbar"><ul><li><button id="newbutton" type=' +
'"button" class="hidden">New</button></li></ul></div></nav>' '"button" class="hidden">New</button></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$('.navbar-toggle').hasClass('collapsed') && query('.navbar-toggle').classList.contains('collapsed') &&
$('#navbar').attr('aria-expanded') !== 'true' query('#navbar').getAttribute('aria-expanded') !== 'true'
); );
$.PrivateBin.TopNav.collapseBar(); PrivateBin.TopNav.collapseBar();
results.push( results.push(
$('.navbar-toggle').hasClass('collapsed') && query('.navbar-toggle').classList.contains('collapsed') &&
$('#navbar').attr('aria-expanded') !== 'true' query('#navbar').getAttribute('aria-expanded') !== 'true'
); );
/* /*
with the upgrade for bootstrap-3.3.7.js to bootstrap-3.4.1.js with the upgrade for bootstrap-3.3.7.js to bootstrap-3.4.1.js
the mobile interface detection changed to check if the the mobile interface detection changed to check if the
ontouchstart event exists, which broke this section of the test ontouchstart event exists, which broke this section of the test
$('.navbar-toggle').trigger('click'); const toggleBtn = document.querySelector('.navbar-toggle');
toggleBtn.dispatchEvent(new MouseEvent('click'));
results.push( results.push(
!$('.navbar-toggle').hasClass('collapsed') && !toggleBtn.classList.contains('collapsed') &&
$('#navbar').attr('aria-expanded') == 'true' document.getElementById('navbar').getAttribute('aria-expanded') == 'true'
); );
$.PrivateBin.TopNav.collapseBar(); PrivateBin.TopNav.collapseBar();
results.push( results.push(
$('.navbar-toggle').hasClass('collapsed') && document.querySelector('.navbar-toggle').classList.contains('collapsed') &&
$('#navbar').attr('aria-expanded') == 'false' document.getElementById('navbar').getAttribute('aria-expanded') == 'false'
); );
*/ */
clean();
const result = results.every(element => element); const result = results.every(element => element);
if (!result) { if (!result) {
console.log(results); console.log(results);
@@ -364,36 +364,35 @@ describe('TopNav', function () {
'reset inputs to defaults (options off)', 'reset inputs to defaults (options off)',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' + '<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="burnafterreading" name="burnafterreading" /> ' + 'id="burnafterreading" name="burnafterreading" /> ' +
'Burn after reading</label></li><li id="opendiscussionoption" ' + 'Burn after reading</label></li><li id="opendiscussionoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="opendiscussion" name="opendiscussion" /> ' + 'id="opendiscussion" name="opendiscussion" /> ' +
'Open discussion</label></li></ul></div></nav>' 'Open discussion</label></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
$('#burnafterreading').attr('checked', 'checked'); query('#burnafterreading').checked = true;
$('#opendiscussion').attr('checked', 'checked'); query('#opendiscussion').checked = true;
results.push( results.push(
$.PrivateBin.TopNav.getBurnAfterReading() PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
$.PrivateBin.TopNav.getOpenDiscussion() PrivateBin.TopNav.getOpenDiscussion()
); );
$.PrivateBin.TopNav.resetInput(); PrivateBin.TopNav.resetInput();
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -408,35 +407,35 @@ describe('TopNav', function () {
'reset inputs to defaults (burnafterreading on)', 'reset inputs to defaults (burnafterreading on)',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' + '<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="burnafterreading" name="burnafterreading" checked="checked" /> ' + 'id="burnafterreading" name="burnafterreading" checked="checked" /> ' +
'Burn after reading</label></li><li id="opendiscussionoption" ' + 'Burn after reading</label></li><li id="opendiscussionoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="opendiscussion" name="opendiscussion" checked="checked" /> ' + 'id="opendiscussion" name="opendiscussion" checked="checked" /> ' +
'Open discussion</label></li></ul></div></nav>' 'Open discussion</label></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$.PrivateBin.TopNav.getBurnAfterReading() PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
$('#burnafterreading').removeAttr('checked'); query('#burnafterreading').checked = false;
query('#burnafterreading').removeAttribute('checked');
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
$.PrivateBin.TopNav.resetInput(); PrivateBin.TopNav.resetInput();
results.push( results.push(
$.PrivateBin.TopNav.getBurnAfterReading() PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -451,36 +450,37 @@ describe('TopNav', function () {
'reset inputs to defaults (opendiscussion on)', 'reset inputs to defaults (opendiscussion on)',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' + '<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="burnafterreading" name="burnafterreading" /> ' + 'id="burnafterreading" name="burnafterreading" /> ' +
'Burn after reading</label></li><li id="opendiscussionoption" ' + 'Burn after reading</label></li><li id="opendiscussionoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="opendiscussion" name="opendiscussion" checked="checked" /> ' + 'id="opendiscussion" name="opendiscussion" checked="checked" /> ' +
'Open discussion</label></li></ul></div></nav>' 'Open discussion</label></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
$.PrivateBin.TopNav.getOpenDiscussion() PrivateBin.TopNav.getOpenDiscussion()
); );
$('#opendiscussion').removeAttr('checked'); query('#opendiscussion').checked = false;
$('#burnafterreading').prop('checked', true); query('#opendiscussion').removeAttribute('checked');
query('#burnafterreading').checked = true;
query('#burnafterreading').setAttribute('checked', 'checked');
results.push( results.push(
$.PrivateBin.TopNav.getBurnAfterReading() PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
$.PrivateBin.TopNav.resetInput(); PrivateBin.TopNav.resetInput();
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
results.push( results.push(
$.PrivateBin.TopNav.getOpenDiscussion() PrivateBin.TopNav.getOpenDiscussion()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -500,13 +500,12 @@ describe('TopNav', function () {
it( it(
'returns the currently selected expiration date', 'returns the currently selected expiration date',
function () { function () {
$('body').html( document.documentElement.innerHTML =
'<select id="pasteExpiration" name="pasteExpiration">' + '<select id="pasteExpiration" name="pasteExpiration">' +
'<option value="1day">1 day</option>' + '<option value="1day">1 day</option>' +
'<option value="never">Never</option></select>' '<option value="never">Never</option></select>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init(); assert.strictEqual(PrivateBin.TopNav.getExpiration(), '1day');
assert.strictEqual($.PrivateBin.TopNav.getExpiration(), '1day');
cleanup(); cleanup();
} }
); );
@@ -558,7 +557,7 @@ describe('TopNav', function () {
'returns the selected files', 'returns the selected files',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' + '<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' + 'dropdown"><a href="#" class="dropdown-toggle" data-' +
'toggle="dropdown" role="button" aria-haspopup="true" ' + 'toggle="dropdown" role="button" aria-haspopup="true" ' +
@@ -567,17 +566,16 @@ describe('TopNav', function () {
'<div><input type="file" id="file" name="file" /></div>' + '<div><input type="file" id="file" name="file" /></div>' +
'</li><li id="customattachment" class="hidden"></li><li>' + '</li><li id="customattachment" class="hidden"></li><li>' +
'<a id="fileremovebutton" href="#">Remove attachment</a>' + '<a id="fileremovebutton" href="#">Remove attachment</a>' +
'</li></ul></li></ul></div></nav>' '</li></ul></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$.PrivateBin.TopNav.getFileList() === null PrivateBin.TopNav.getFileList() === null
); );
addFileList($('#file')[0], [ addFileList(query('#file'), [
'../img/logo.svg', '../img/logo.svg',
'../img/busy.gif' '../img/busy.gif'
]); ]);
const files = $.PrivateBin.TopNav.getFileList(); const files = PrivateBin.TopNav.getFileList();
results.push( results.push(
files[0].name === 'logo.svg' && files[0].name === 'logo.svg' &&
files[1].name === 'busy.gif' files[1].name === 'busy.gif'
@@ -601,23 +599,23 @@ describe('TopNav', function () {
'returns if the burn-after-reading checkbox was ticked', 'returns if the burn-after-reading checkbox was ticked',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' + '<nav><div id="navbar"><ul><li id="burnafterreadingoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="burnafterreading" name="burnafterreading" /> ' + 'id="burnafterreading" name="burnafterreading" /> ' +
'Burn after reading</label></li></ul></div></nav>' 'Burn after reading</label></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
$('#burnafterreading').attr('checked', 'checked'); query('#burnafterreading').checked = true;
results.push( results.push(
$.PrivateBin.TopNav.getBurnAfterReading() PrivateBin.TopNav.getBurnAfterReading()
); );
$('#burnafterreading').removeAttr('checked'); query('#burnafterreading').checked = false;
query('#burnafterreading').removeAttribute('checked');
results.push( results.push(
!$.PrivateBin.TopNav.getBurnAfterReading() !PrivateBin.TopNav.getBurnAfterReading()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -638,23 +636,23 @@ describe('TopNav', function () {
'returns if the open-discussion checkbox was ticked', 'returns if the open-discussion checkbox was ticked',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="opendiscussionoption" ' + '<nav><div id="navbar"><ul><li id="opendiscussionoption" ' +
'class="hidden"><label><input type="checkbox" ' + 'class="hidden"><label><input type="checkbox" ' +
'id="opendiscussion" name="opendiscussion" /> ' + 'id="opendiscussion" name="opendiscussion" /> ' +
'Open discussion</label></li></ul></div></nav>' 'Open discussion</label></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
$('#opendiscussion').attr('checked', 'checked'); query('#opendiscussion').checked = true;
results.push( results.push(
$.PrivateBin.TopNav.getOpenDiscussion() PrivateBin.TopNav.getOpenDiscussion()
); );
$('#opendiscussion').removeAttr('checked'); query('#opendiscussion').checked = false;
query('#opendiscussion').removeAttribute('checked');
results.push( results.push(
!$.PrivateBin.TopNav.getOpenDiscussion() !PrivateBin.TopNav.getOpenDiscussion()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -677,23 +675,22 @@ describe('TopNav', function () {
function (password) { function (password) {
password = password.replace(/\r+|\n+/g, ''); password = password.replace(/\r+|\n+/g, '');
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li><div id="password" ' + '<nav><div id="navbar"><ul><li><div id="password" ' +
'class="navbar-form hidden"><input type="password" ' + 'class="navbar-form hidden"><input type="password" ' +
'id="passwordinput" placeholder="Password (recommended)" ' + 'id="passwordinput" placeholder="Password (recommended)" ' +
'class="form-control" size="23" /></div></li></ul></div></nav>' 'class="form-control" size="23" /></div></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
$.PrivateBin.TopNav.getPassword() === '' PrivateBin.TopNav.getPassword() === ''
); );
$('#passwordinput').val(password); query('#passwordinput').value = password;
results.push( results.push(
$.PrivateBin.TopNav.getPassword() === password PrivateBin.TopNav.getPassword() === password
); );
$('#passwordinput').val(''); query('#passwordinput').value = '';
results.push( results.push(
$.PrivateBin.TopNav.getPassword() === '' PrivateBin.TopNav.getPassword() === ''
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -714,7 +711,7 @@ describe('TopNav', function () {
'returns the custom attachment element', 'returns the custom attachment element',
function () { function () {
let results = []; let results = [];
$('body').html( document.documentElement.innerHTML =
'<nav><div id="navbar"><ul><li id="attach" class="hidden ' + '<nav><div id="navbar"><ul><li id="attach" class="hidden ' +
'dropdown"><a href="#" class="dropdown-toggle" data-' + 'dropdown"><a href="#" class="dropdown-toggle" data-' +
'toggle="dropdown" role="button" aria-haspopup="true" ' + 'toggle="dropdown" role="button" aria-haspopup="true" ' +
@@ -723,15 +720,14 @@ describe('TopNav', function () {
'<div><input type="file" id="file" name="file" /></div>' + '<div><input type="file" id="file" name="file" /></div>' +
'</li><li id="customattachment" class="hidden"></li><li>' + '</li><li id="customattachment" class="hidden"></li><li>' +
'<a id="fileremovebutton" href="#">Remove attachment</a>' + '<a id="fileremovebutton" href="#">Remove attachment</a>' +
'</li></ul></li></ul></div></nav>' '</li></ul></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init();
results.push( results.push(
!$.PrivateBin.TopNav.getCustomAttachment().hasClass('test') !PrivateBin.TopNav.getCustomAttachment().classList.contains('test')
); );
$('#customattachment').addClass('test'); query('#customattachment').classList.add('test');
results.push( results.push(
$.PrivateBin.TopNav.getCustomAttachment().hasClass('test') PrivateBin.TopNav.getCustomAttachment().classList.contains('test')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -753,7 +749,7 @@ describe('TopNav', function () {
function () { function () {
// Insert any setup code needed for the hideAllButtons function // Insert any setup code needed for the hideAllButtons function
// Example: Initialize the DOM elements required for testing // Example: Initialize the DOM elements required for testing
$('body').html( document.body.innerHTML =
'<nav class="navbar navbar-inverse navbar-static-top">' + '<nav class="navbar navbar-inverse navbar-static-top">' +
'<div id="navbar" class="navbar-collapse collapse"><ul ' + '<div id="navbar" class="navbar-collapse collapse"><ul ' +
'class="nav navbar-nav"><li><button id="newbutton" ' + 'class="nav navbar-nav"><li><button id="newbutton" ' +
@@ -770,15 +766,14 @@ describe('TopNav', function () {
'data-toggle="modal" data-target="#qrcodemodal" ' + 'data-toggle="modal" data-target="#qrcodemodal" ' +
'class="hidden btn btn-warning navbar-btn"><span ' + 'class="hidden btn btn-warning navbar-btn"><span ' +
'class="glyphicon glyphicon-qrcode" aria-hidden="true">' + 'class="glyphicon glyphicon-qrcode" aria-hidden="true">' +
'</span> QR code</button></li></ul></div></nav>' '</span> QR code</button></li></ul></div></nav>';
); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init(); PrivateBin.TopNav.hideAllButtons();
$.PrivateBin.TopNav.hideAllButtons();
assert.ok($('#newbutton').hasClass('hidden')); assert.ok(query('#newbutton').classList.contains('hidden'));
assert.ok($('#clonebutton').hasClass('hidden')); assert.ok(query('#clonebutton').classList.contains('hidden'));
assert.ok($('#rawtextbutton').hasClass('hidden')); assert.ok(query('#rawtextbutton').classList.contains('hidden'));
assert.ok($('#qrcodelink').hasClass('hidden')); assert.ok(query('#qrcodelink').classList.contains('hidden'));
cleanup(); cleanup();
} }
); );
@@ -793,14 +788,17 @@ describe('TopNav', function () {
it( it(
'displays raw text view correctly', 'displays raw text view correctly',
function () { function () {
const clean = jsdom('', {url: 'https://privatebin.net/?0123456789abcdef#1'}); const clean = globalThis.cleanup('', {url: 'https://privatebin.net/?0123456789abcdef#1'});
$('body').html('<button id="rawtextbutton"></button>'); document.documentElement.innerHTML = `
<li id="loadingindicator" class="hidden"></li>
<button id="rawtextbutton"></button>`;
const sample = 'example'; const sample = 'example';
$.PrivateBin.PasteViewer.setText(sample); PrivateBin.Alert.init(); // required because of locading indiator being used
$.PrivateBin.Helper.reset(); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.init(); PrivateBin.PasteViewer.setText(sample);
$('#rawtextbutton').click(); PrivateBin.Helper.reset();
assert.strictEqual($('pre').text(), sample); query('#rawtextbutton').click();
assert.strictEqual(document.querySelector('pre').textContent, sample);
clean(); clean();
} }
); );

View File

@@ -7,7 +7,7 @@ describe('UiHelper', function () {
describe('historyChange', function () { describe('historyChange', function () {
this.timeout(30000); this.timeout(30000);
beforeEach(function () { beforeEach(function () {
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
cleanup(); cleanup();
}); });
@@ -16,10 +16,10 @@ describe('UiHelper', function () {
common.jscUrl(false, false), common.jscUrl(false, false),
function (url) { function (url) {
const expected = common.urlToString(url), const expected = common.urlToString(url),
clean = jsdom('', {url: expected}); clean = globalThis.cleanup('', {url: expected});
$.PrivateBin.UiHelper.mockHistoryChange(); PrivateBin.UiHelper.mockHistoryChange();
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
var result = window.location.href; var result = window.location.href;
clean(); clean();
return expected === result; return expected === result;
@@ -33,12 +33,12 @@ describe('UiHelper', function () {
function (url, fragment) { function (url, fragment) {
url.fragment = fragment.join(''); url.fragment = fragment.join('');
const expected = common.urlToString(url), const expected = common.urlToString(url),
clean = jsdom('', {url: expected}); clean = globalThis.cleanup('', {url: expected});
$.PrivateBin.UiHelper.mockHistoryChange([ PrivateBin.UiHelper.mockHistoryChange([
{type: 'newpaste'}, '', expected {type: 'newpaste'}, '', expected
]); ]);
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
var result = window.location.href; var result = window.location.href;
clean(); clean();
return expected === result; return expected === result;
@@ -51,20 +51,20 @@ describe('UiHelper', function () {
/* /*
this.timeout(30000); this.timeout(30000);
before(function () { before(function () {
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
}); });
jsc.property( jsc.property(
'redirects to home', 'redirects to home',
common.jscUrl(), common.jscUrl(),
function (url) { function (url) {
const clean = jsdom('', {url: common.urlToString(url)}); const clean = globalThis.cleanup('', {url: common.urlToString(url)});
delete(url.query); delete(url.query);
delete(url.fragment); delete(url.fragment);
const expected = common.urlToString(url); const expected = common.urlToString(url);
$.PrivateBin.UiHelper.reloadHome(); PrivateBin.UiHelper.reloadHome();
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
var result = window.location.href; var result = window.location.href;
clean(); clean();
return expected === result; return expected === result;
@@ -78,7 +78,7 @@ describe('UiHelper', function () {
// once it is supported or a workaround is found, uncomment the section below // once it is supported or a workaround is found, uncomment the section below
/* /*
before(function () { before(function () {
$.PrivateBin.Helper.reset(); PrivateBin.Helper.reset();
}); });
jsc.property( jsc.property(
@@ -88,10 +88,10 @@ describe('UiHelper', function () {
function (id, element) { function (id, element) {
id = id.join(''); id = id.join('');
element = element.join(''); element = element.join('');
var clean = jsdom( var clean = globalThis.cleanup(
'<' + element + ' id="' + id + '"></' + element + '>' '<' + element + ' id="' + id + '"></' + element + '>'
); );
var result = $.PrivateBin.UiHelper.isVisible($('#' + id)); var result = PrivateBin.UiHelper.isVisible(document.getElementById(id));
clean(); clean();
return result; return result;
} }

View File

@@ -3,7 +3,7 @@ require('../common');
// DOM builder that mirrors bootstrap5.php navbar // DOM builder that mirrors bootstrap5.php navbar
function buildEmailDomNoShortUrl() { function buildEmailDomNoShortUrl() {
$('body').html( document.documentElement.innerHTML =
// TopNav expects initially hidden #emaillink BUTTON. // TopNav expects initially hidden #emaillink BUTTON.
'<nav><div id="navbar"><ul>' + '<nav><div id="navbar"><ul>' +
'<li>' + '<li>' +
@@ -15,14 +15,18 @@ function buildEmailDomNoShortUrl() {
'<button id="emaillink" type="button" class="hidden btn btn-secondary">Email</button>' + '<button id="emaillink" type="button" class="hidden btn btn-secondary">Email</button>' +
'</li>' + '</li>' +
'</ul></div></nav>' + '</ul></div></nav>' +
'<input id="burnafterreadingoption" type="checkbox">' '<input id="burnafterreadingoption" type="checkbox">' +
); // include dummy email confirm modal for sendEmail
'<div id="emailconfirmmodal" class="hidden">' +
'<div id="emailconfirm-timezone-current"></div>' +
'<div id="emailconfirm-timezone-utc"></div>' +
'</div>'
} }
// DOM builder that adds the shortener result block // DOM builder that adds the shortener result block
function buildEmailDomWithShortUrl() { function buildEmailDomWithShortUrl() {
buildEmailDomNoShortUrl(); buildEmailDomNoShortUrl();
$('body').html( document.documentElement.innerHTML =
// TopNav expectsinitially hidden #emaillink BUTTON. // TopNav expectsinitially hidden #emaillink BUTTON.
'<nav><div id="navbar"><ul>' + '<nav><div id="navbar"><ul>' +
'<li>' + '<li>' +
@@ -38,41 +42,41 @@ function buildEmailDomWithShortUrl() {
'<div id="pastelink">Your document is ' + '<div id="pastelink">Your document is ' +
'<a id="pasteurl" href="https://short.example/xYz">https://short.example/xYz</a> ' + '<a id="pasteurl" href="https://short.example/xYz">https://short.example/xYz</a> ' +
'<span id="copyhint">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>' + '<span id="copyhint">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>' +
'</div>' +
// add a minimal email confirmation modal so sendEmail does not crash
'<div id="emailconfirmmodal" class="hidden">' +
'<div id="emailconfirm-timezone-current"></div>' +
'<div id="emailconfirm-timezone-utc"></div>' +
'</div>' '</div>'
);
} }
function stubWinOpen($element) { function makeWindowOpenMock() {
const win = $element[0].ownerDocument.defaultView; const originalOpen = window.open;
// Some helpers in privatebin.js expect a global document.
global.document = win.document;
let openedUrl = null; let openedUrl = null;
const origOpen = win.open; let mockRestoreFn = null;
// Prefer simple assignment; if blocked, fall back to defineProperty. if (typeof jest !== 'undefined' && typeof jest.spyOn === 'function') {
try { const spy = jest.spyOn(window, 'open').mockImplementation((url) => {
win.open = function (url) { openedUrl = url;
return {};
});
mockRestoreFn = () => spy.mockRestore();
} else {
window.open = function (url) {
openedUrl = url; openedUrl = url;
return {}; return {};
}; };
} catch (e) { mockRestoreFn = () => { window.open = originalOpen; };
Object.defineProperty(win, 'open', {
value: function (url) {
openedUrl = url;
return {};
},
configurable: true,
writable: true
});
} }
return { return {
getUrl: () => openedUrl, getUrl: () => openedUrl,
restore: () => { try { win.open = origOpen; } catch (e) { /* suppress exception in restore */ } }, restore: () => {
win if (mockRestoreFn) {
mockRestoreFn();
}
}
}; };
} }
@@ -84,21 +88,23 @@ function extractMailtoBody(mailtoUrl) {
} }
describe('Email - mail body content (short URL vs. fallback)', function () { describe('Email - mail body content (short URL vs. fallback)', function () {
before(function () { beforeEach(function () {
cleanup(); // provided by common cleanup(); // provided by common
}); });
it('Uses the short URL when #pasteurl is present and never includes "undefined"', function () { it('Uses the short URL when #pasteurl is present and never includes "undefined"', function () {
buildEmailDomWithShortUrl(); // with #pastelink/#pasteurl buildEmailDomWithShortUrl(); // with #pastelink/#pasteurl
$.PrivateBin.TopNav.init(); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.showEmailButton(0); PrivateBin.TopNav.showEmailButton(0);
const $emailBtn = $('#emaillink'); const emailBtn = document.getElementById('emaillink');
assert.ok(!$emailBtn.hasClass('hidden'), '#emaillink should be visible after showEmailButton'); assert.ok(!emailBtn.classList.contains('hidden'), '#emaillink should be visible after showEmailButton');
const { getUrl, restore } = stubWinOpen($emailBtn); const { getUrl, restore } = makeWindowOpenMock();
try { try {
$emailBtn.trigger('click'); emailBtn.click();
document.getElementById('emailconfirm-timezone-current').click();
const openedUrl = getUrl(); const openedUrl = getUrl();
assert.ok(openedUrl, 'window.open should have been called'); assert.ok(openedUrl, 'window.open should have been called');
@@ -107,30 +113,30 @@ describe('Email - mail body content (short URL vs. fallback)', function () {
assert.doesNotMatch(body, /undefined/, 'email body must not contain "undefined"'); assert.doesNotMatch(body, /undefined/, 'email body must not contain "undefined"');
} finally { } finally {
restore(); restore();
cleanup();
} }
}); });
it('Falls back to window.location.href when #pasteurl is absent and never includes "undefined"', function () { it('Falls back to window.location.href when #pasteurl is absent and never includes "undefined"', function () {
buildEmailDomNoShortUrl(); // No #pasteurl buildEmailDomNoShortUrl(); // No #pasteurl
$.PrivateBin.TopNav.init(); PrivateBin.TopNav.init();
$.PrivateBin.TopNav.showEmailButton(0); PrivateBin.TopNav.showEmailButton(0);
const $emailBtn = $('#emaillink'); const emailBtn = document.getElementById('emaillink');
assert.ok(!$emailBtn.hasClass('hidden'), '#emaillink should be visible after showEmailButton'); assert.ok(!emailBtn.classList.contains('hidden'), '#emaillink should be visible after showEmailButton');
const { getUrl, restore, win } = stubWinOpen($emailBtn); const { getUrl, restore } = makeWindowOpenMock();
try { try {
$emailBtn.trigger('click'); emailBtn.click();
document.getElementById('emailconfirm-timezone-current').click();
const openedUrl = getUrl(); const openedUrl = getUrl();
assert.ok(openedUrl, 'window.open should have been called'); assert.ok(openedUrl, 'window.open should have been called');
const body = extractMailtoBody(openedUrl); const body = extractMailtoBody(openedUrl);
assert.match(body, new RegExp(win.location.href), 'email body should include the fallback page URL'); assert.match(body, new RegExp(window.location.href), 'email body should include the fallback page URL');
assert.doesNotMatch(body, /undefined/, 'email body must not contain "undefined"'); assert.doesNotMatch(body, /undefined/, 'email body must not contain "undefined"');
} finally { } finally {
restore(); restore();
cleanup();
} }
}); });
}); });

View File

@@ -122,7 +122,7 @@ class Configuration
'js/kjua-0.10.0.js' => 'sha512-BYj4xggowR7QD150VLSTRlzH62YPfhpIM+b/1EUEr7RQpdWAGKulxWnOvjFx1FUlba4m6ihpNYuQab51H6XlYg==', 'js/kjua-0.10.0.js' => 'sha512-BYj4xggowR7QD150VLSTRlzH62YPfhpIM+b/1EUEr7RQpdWAGKulxWnOvjFx1FUlba4m6ihpNYuQab51H6XlYg==',
'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==', 'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==',
'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==',
'js/privatebin.js' => 'sha512-6SwOJniNN8RBmAK7yCt4ly2qYyH8OALxB74/K1AJgw+YnZgRCfTDVq1qY1K5Y2QCxCODGGTpAjTqQRExzCqV7g==', 'js/privatebin.js' => 'sha512-1nnRQdjFEp16n/ogNB9UcDmHrLwn+tvu+X1aA6nDDadOFAfujyhg+8qAdZqdZRocSTWY844Wk6FCpv7FnZUrgg==',
'js/purify-3.3.0.js' => 'sha512-lsHD5zxs4lu/NDzaaibe27Vd2t7Cy9JQ3qDHUvDfb4oZvKoWDNEhwUY+4bT3R68cGgpgCYp8U1x2ifeVxqurdQ==', 'js/purify-3.3.0.js' => 'sha512-lsHD5zxs4lu/NDzaaibe27Vd2t7Cy9JQ3qDHUvDfb4oZvKoWDNEhwUY+4bT3R68cGgpgCYp8U1x2ifeVxqurdQ==',
'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==',
'js/zlib-1.3.1-2.js' => 'sha512-4gT+v+BkBqdVBbKOO4qKGOAzuay+v1FmOLksS+bMgQ08Oo4xEb3X48Xq1Kv2b4HtiCQA7xq9dFRzxal7jmQI7w==', 'js/zlib-1.3.1-2.js' => 'sha512-4gT+v+BkBqdVBbKOO4qKGOAzuay+v1FmOLksS+bMgQ08Oo4xEb3X48Xq1Kv2b4HtiCQA7xq9dFRzxal7jmQI7w==',