Compare commits

..

49 Commits

Author SHA1 Message Date
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
33 changed files with 2127 additions and 2013 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

@@ -6,10 +6,3 @@ Have a look at our [contributing guide](https://github.com/PrivateBin/PrivateBin
If you want to translate PrivateBin into your language have a look at the [translation guide](https://github.com/PrivateBin/PrivateBin/wiki/Translation). If you want to translate PrivateBin into your language have a look at the [translation guide](https://github.com/PrivateBin/PrivateBin/wiki/Translation).
Except this also opening [issues](https://github.com/PrivateBin/PrivateBin/issues) helps much. Just describe your problem detailed enough and fill out our template. Except this also opening [issues](https://github.com/PrivateBin/PrivateBin/issues) helps much. Just describe your problem detailed enough and fill out our template.
## Guidelines for pull requests
Please note that we, as per our pull request template, **require users to disclose the use of an AI/LLM tool**. We would be glad about details such as the exact used tool/relevant chat snippets or a full (link to the) chat conversation. In any case, please take care to manually test and review your pull request.
For Frontend adjustments or other changes visible visually in the PrivateBin web UI, please _always_ attach at least **a screenshot** of how it looks like with your changes applied. You may only omit that for invisible changes or _very_ obvious little changes like fixing typographic mistakes or translations etc.
If possible, especially for bigger or interactive changes, please also attach a link to a working test instance.

View File

@@ -1,4 +1,4 @@
<!--Please honor our guidelines as written in the CONTRIBUTING.md file. To emphasize: it is required to disclose the usage of an LLM tool. --> <!-- This is a template for your Pull Request. This are just some suggestions for you. You do not have to use all of them. -->
<!-- If your PR fixes an issue, mention it here. You can also just copy the URL - GitHub will convert it for you. <!-- If your PR fixes an issue, mention it here. You can also just copy the URL - GitHub will convert it for you.
If this PR fixes several issues, please prepend each issue url/number with the word "fix"/"fixes" or "close"/"closes" as this automatically closes the issues you mentioned when the PR is merged. If this PR fixes several issues, please prepend each issue url/number with the word "fix"/"fixes" or "close"/"closes" as this automatically closes the issues you mentioned when the PR is merged.
@@ -6,12 +6,11 @@ If this PR fixes several issues, please prepend each issue url/number with the w
This PR fixes This PR fixes
## Changes ## Changes
<!-- List all the changes you have done. This section is just an example and may be removed if irrelevant. --> <!-- List all the changes you have done -->
* *
* *
## ToDo ## ToDo
<!-- This section is just an example and may be removed if irrelevant, e.g. if you have completely implemented the PR. -->
* [ ] * [ ]
* [ ] * [ ]
* [ ] * [ ]

View File

@@ -25,7 +25,7 @@ jobs:
steps: steps:
- name: Download and Extract Artifacts - name: Download and Extract Artifacts
uses: dawidd6/action-download-artifact@8a338493df3d275e4a7a63bcff3b8fe97e51a927 uses: dawidd6/action-download-artifact@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3
with: with:
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
path: artifacts path: artifacts

View File

@@ -4,7 +4,7 @@
* ADDED: Translations for Swedish & Persian * ADDED: Translations for Swedish & Persian
* CHANGED: Deduplicate JSON error message translations * CHANGED: Deduplicate JSON error message translations
* CHANGED: Refactored translation of exception messages * CHANGED: Refactored translation of exception messages
* CHANGED: Upgrading libraries to: DOMpurify 3.3.2, ip-lib 1.22.0 & polyfill-php80 1.33.0 * CHANGED: Upgrading libraries to: ip-lib 1.22.0 & polyfill-php80 1.33.0
* FIXED: Some exceptions not getting translated * FIXED: Some exceptions not getting translated
* FIXED: Attachment disappears after a "paste" in the message area (#1731) * FIXED: Attachment disappears after a "paste" in the message area (#1731)
* FIXED: The content format is not reset when creating a new document (#1707) * FIXED: The content format is not reset when creating a new document (#1707)

View File

@@ -1,6 +1,6 @@
{ {
"PrivateBin": "PrivateBin", "PrivateBin": "PrivateBin",
"%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s je minimalističen, odprtokodni spletni 'pastebin', kjer strežnik ne ve ničesar o prilepljenih podatkih. Podatki so šifrirani/dešifrirani %sv brskalniku%s z uporabo 256 bitnega AES.", "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s je minimalističen, odprtokodni spletni 'pastebin', kjer server ne ve ničesar o prilepljenih podatkih. Podatki so zakodirani/odkodirani %sv brskalniku%s z uporabo 256 bitnega AES.",
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Več informacij na <a href=\"https://privatebin.info/\">spletni strani projekta.</a>.", "More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Več informacij na <a href=\"https://privatebin.info/\">spletni strani projekta.</a>.",
"Because ignorance is bliss": "Ker kar ne veš ne boli.", "Because ignorance is bliss": "Ker kar ne veš ne boli.",
"Document does not exist, has expired or has been deleted.": "Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.", "Document does not exist, has expired or has been deleted.": "Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.",
@@ -154,7 +154,7 @@
"Your document is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "Tvoj prilepek je dostopen na naslovu: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pritisni <kbd>Ctrl</kbd>+<kbd>c</kbd> ali [Cmd] + [c] in skopiraj)</span>", "Your document is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "Tvoj prilepek je dostopen na naslovu: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pritisni <kbd>Ctrl</kbd>+<kbd>c</kbd> ali [Cmd] + [c] in skopiraj)</span>",
"Delete data": "Izbriši podatke", "Delete data": "Izbriši podatke",
"Could not create document: %s": "Ne morem ustvariti prilepka: %s", "Could not create document: %s": "Ne morem ustvariti prilepka: %s",
"Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Prilepka ni mogoče odkodirati: v URL-ju manjka ključ. Ali je bil uporabljen krajšalnik URL-jev, ki odstrani del URL-ja?", "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Ne morem odkodirati prilepka: V URL-ju manjka ključ (A si uporabil krajšalnik URL-jev, ki odstrani del URL-ja?)",
"B": "o", "B": "o",
"kB": "kB", "kB": "kB",
"MB": "MB", "MB": "MB",
@@ -191,17 +191,17 @@
"+++ no document text +++": "+++ ni besedila dokumenta +++", "+++ no document text +++": "+++ ni besedila dokumenta +++",
"Could not get document data: %s": "Podatkov dokumenta ni bilo mogoče pridobiti: %s", "Could not get document data: %s": "Podatkov dokumenta ni bilo mogoče pridobiti: %s",
"QR code": "QR koda", "QR code": "QR koda",
"This website is using an insecure HTTP connection! Please use it only for testing.": "To spletno mesto uporablja nezaščiteno povezavo HTTP! Prosim, uporabite jo samo za testiranje.", "This website is using an insecure HTTP connection! Please use it only for testing.": "To spletno mesto uporablja nezaščiteno povezavo HTTP! Prosimo, uporabite jo samo za testiranje.",
"For more information <a href=\"%s\">see this FAQ entry</a>.": "Za več informacij <a href=\"%s\">glejte ta vnos s pogostimi vprašanji</a>.", "For more information <a href=\"%s\">see this FAQ entry</a>.": "Za več informacij <a href=\"%s\">glejte ta vnos s pogostimi vprašanji</a>.",
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Vaš brskalnik morda zahteva povezavo HTTPS za podporo API-ja WebCrypto. Poskusite <a href=\"%s\">preklopiti na HTTPS</a>.", "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Vaš brskalnik morda zahteva povezavo HTTPS za podporo API-ja WebCrypto. Poskusite <a href=\"%s\">preklopiti na HTTPS</a>.",
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Vaš brskalnik ne podpira WebAssembly, ki se uporablja za stiskanje z zlib. Lahko ustvarite nestisnjene dokumente, vendar stisnjenih ne morete odpreti.", "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Vaš brskalnik ne podpira WebAssemblyja, ki se uporablja za stiskanje zlib. Nestisnjene dokumente lahko ustvarite, stisnjenih pa ne morete brati.",
"waiting on user to provide a password": "čakanje na uporabnika, da vnese geslo", "waiting on user to provide a password": "čakanje na uporabnika, da vnese geslo",
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Podatkov ni bilo mogoče dešifrirati. Ste vnesli napačno geslo? Poskusite znova z gumbom na vrhu.", "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Podatkov ni bilo mogoče dešifrirati. Ste vnesli napačno geslo? Poskusite znova z gumbom na vrhu.",
"Retry": "Poskusi ponovno", "Retry": "Poskusi ponovno",
"Showing raw text…": "Prikaz surovega besedila…", "Showing raw text…": "Prikaz surovega besedila…",
"Notice:": "Obvestilo:", "Notice:": "Obvestilo:",
"This link will expire after %s.": "Ta povezava bo potekla čez %s.", "This link will expire after %s.": "Ta povezava bo potekla čez %s.",
"This link can only be accessed once, do not use back or refresh button in your browser.": "Ta povezava je dostopna samo enkrat; v brskalniku ne uporabljajte gumba »Nazaj« ali »Osveži«.", "This link can only be accessed once, do not use back or refresh button in your browser.": "Do te povezave lahko dostopate samo enkrat, ne uporabljajte gumba za nazaj ali osvežitev v brskalniku.",
"Link:": "Povezava:", "Link:": "Povezava:",
"Recipient may become aware of your timezone, convert time to UTC?": "Prejemnik lahko izve vaš časovni pas in pretvori čas v UTC?", "Recipient may become aware of your timezone, convert time to UTC?": "Prejemnik lahko izve vaš časovni pas in pretvori čas v UTC?",
"Use Current Timezone": "Uporabi trenutni časovni pas", "Use Current Timezone": "Uporabi trenutni časovni pas",
@@ -213,14 +213,14 @@
"URL shortener is enabled by default.": "Okrajševalec URL-jev je privzeto omogočen.", "URL shortener is enabled by default.": "Okrajševalec URL-jev je privzeto omogočen.",
"Save document": "Shrani dokument", "Save document": "Shrani dokument",
"Your IP is not authorized to create documents.": "Vaš IP ni pooblaščen za ustvarjanje dokumentov.", "Your IP is not authorized to create documents.": "Vaš IP ni pooblaščen za ustvarjanje dokumentov.",
"Trying to shorten a URL that isn't pointing at our instance.": "Poskus krajšanja URL-ja, ki ne kaže na našo instanco.", "Trying to shorten a URL that isn't pointing at our instance.": "Poskus skrajšanja URL, ki ne kaže na naš primerek.",
"Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Napaka posredniškega strežnika: URL posredniškega strežnika je prazen. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi.", "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Napaka posredniškega strežnika: URL posredniškega strežnika je prazen. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi.",
"Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Napaka posredniškega strežnika: Napaka pri razčlenjevanju odgovora posredniškega strežnika. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi.", "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Napaka posredniškega strežnika: Napaka pri razčlenjevanju odgovora posredniškega strežnika. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi.",
"Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Napaka posredniškega strežnika: Slab odgovor. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi ali začasni izpad.", "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Napaka posredniškega strežnika: Slab odgovor. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi ali začasni izpad.",
"This secret message can only be displayed once. Would you like to see it now?": "To skrivno sporočilo je mogoče prikazati samo enkrat. Ali ga želite videti zdaj?", "This secret message can only be displayed once. Would you like to see it now?": "To skrivno sporočilo je mogoče prikazati samo enkrat. Ali ga želite videti zdaj?",
"Yes, see it": "Da, pokaži", "Yes, see it": "Da, pokaži",
"Dark Mode": "Temni način", "Dark Mode": "Temni način",
"Error compressing document, due to missing WebAssembly support.": "Napaka pri stiskanju pripleka zaradi manjkajoče podpore za WebAssembly.", "Error compressing document, due to missing WebAssembly support.": "Napaka pri stiskanju dokumenta zaradi manjkajoče podpore za WebAssembly.",
"Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Napaka pri razpakiranju dokumenta, vaš brskalnik ne podpira WebAssembly. Za ogled tega dokumenta uporabite drug brskalnik.", "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Napaka pri razpakiranju dokumenta, vaš brskalnik ne podpira WebAssembly. Za ogled tega dokumenta uporabite drug brskalnik.",
"Start over": "Začni znova", "Start over": "Začni znova",
"Document copied to clipboard": "Dokument kopiran v odložišče", "Document copied to clipboard": "Dokument kopiran v odložišče",

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.2'); 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/*"
]
}

15
js/package-lock.json generated
View File

@@ -1853,10 +1853,11 @@
} }
}, },
"node_modules/flatted": { "node_modules/flatted": {
"version": "3.4.2", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true "dev": true,
"license": "ISC"
}, },
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.3.1", "version": "3.3.1",
@@ -5735,9 +5736,9 @@
} }
}, },
"flatted": { "flatted": {
"version": "3.4.2", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true "dev": true
}, },
"foreground-child": { "foreground-child": {

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

File diff suppressed because one or more lines are too long

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,91 @@ 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(); 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

@@ -123,7 +123,7 @@ class Configuration
'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-6SwOJniNN8RBmAK7yCt4ly2qYyH8OALxB74/K1AJgw+YnZgRCfTDVq1qY1K5Y2QCxCODGGTpAjTqQRExzCqV7g==',
'js/purify-3.3.2.js' => 'sha512-I6igPVpf3xNghG92mujwqB6Zi3LpUTsni4bRuLnMThEGH6BDbsumv7373+AXHzA4OUlxGsym8ZxKFHy4xjYvkQ==', '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==',
), ),

View File

@@ -65,7 +65,7 @@ if ($MARKDOWN) :
<?php <?php
endif; endif;
?> ?>
<?php $this->_scriptTag('js/purify-3.3.2.js', 'defer'); ?> <?php $this->_scriptTag('js/purify-3.3.0.js', 'defer'); ?>
<?php $this->_scriptTag('js/legacy.js', 'defer'); ?> <?php $this->_scriptTag('js/legacy.js', 'defer'); ?>
<?php $this->_scriptTag('js/privatebin.js', 'defer'); ?> <?php $this->_scriptTag('js/privatebin.js', 'defer'); ?>
<!-- icon --> <!-- icon -->

View File

@@ -49,7 +49,7 @@ if ($MARKDOWN) :
<?php <?php
endif; endif;
?> ?>
<?php $this->_scriptTag('js/purify-3.3.2.js', 'defer'); ?> <?php $this->_scriptTag('js/purify-3.3.0.js', 'defer'); ?>
<?php $this->_scriptTag('js/legacy.js', 'defer'); ?> <?php $this->_scriptTag('js/legacy.js', 'defer'); ?>
<?php $this->_scriptTag('js/privatebin.js', 'defer'); ?> <?php $this->_scriptTag('js/privatebin.js', 'defer'); ?>
<!-- icon --> <!-- icon -->