Compare commits

..

17 Commits

Author SHA1 Message Date
El RIDO
df5f4b1440 Merge pull request #1814 from PrivateBin/dependabot/npm_and_yarn/js/flatted-3.4.2
chore(deps-dev): bump flatted from 3.3.3 to 3.4.2 in /js
2026-03-21 15:49:19 +01:00
dependabot[bot]
7eaa70ae31 chore(deps-dev): bump flatted from 3.3.3 to 3.4.2 in /js
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-21 10:28:50 +00:00
El RIDO
a0bdb2b3c8 Merge pull request #1811 from PrivateBin/dependabot/github_actions/dawidd6/action-download-artifact-19
chore(deps): bump dawidd6/action-download-artifact from 18 to 19
2026-03-18 19:58:33 +01:00
dependabot[bot]
f5543d4317 chore(deps): bump dawidd6/action-download-artifact from 18 to 19
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 18 to 19.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](1f8785ff7a...8a338493df)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-version: '19'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-18 11:52:44 +00:00
El RIDO
4ba870f48a Merge pull request #1809 from PrivateBin/dependabot/github_actions/dawidd6/action-download-artifact-18
chore(deps): bump dawidd6/action-download-artifact from 17 to 18
2026-03-16 22:04:15 +01:00
dependabot[bot]
907c4d75f6 chore(deps): bump dawidd6/action-download-artifact from 17 to 18
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 17 to 18.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](09b07ec687...1f8785ff7a)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-version: '18'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 11:56:20 +00:00
rugk
47d5485a33 Merge pull request #1804 from PrivateBin/docs/contrib-guide
Strengthen AI guidlines & enforce screenshots for visual changes
2026-03-15 15:22:13 +01:00
El RIDO
07eb15eb36 Merge pull request #1808 from PrivateBin/dependabot/github_actions/dawidd6/action-download-artifact-17
chore(deps): bump dawidd6/action-download-artifact from 16 to 17
2026-03-12 14:05:12 +01:00
dependabot[bot]
69ac3ad079 chore(deps): bump dawidd6/action-download-artifact from 16 to 17
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 16 to 17.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](2536c51d3d...09b07ec687)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-version: '17'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-12 11:52:43 +00:00
El RIDO
ab8e2ea2dc Merge pull request #1807 from PrivateBin/purify-3.3.2
update DOMpurify library from 3.3.0 to 3.3.2
2026-03-10 18:41:19 +01:00
El RIDO
604e61beaa update DOMpurify library from 3.3.0 to 3.3.2 2026-03-07 09:00:06 +01:00
El RIDO
6d3c9c9206 Merge pull request #1806 from PrivateBin/crowdin-translation
New Crowdin updates
2026-03-07 08:43:23 +01:00
PrivateBin Translator Bot
1bf20398e3 New translations en.json (Slovenian) 2026-03-05 17:51:08 +01:00
PrivateBin Translator Bot
09f65bf06b New translations en.json (Slovenian) 2026-03-05 14:49:45 +01:00
rugk
d462c201de style: fix typos 2026-03-03 15:21:37 +01:00
rugk
2986c97ea7 docs: also adjust PR template accordingly 2026-03-03 15:19:11 +01:00
rugk
e69570b73e Strengthen AI guidlines & force screenshots for visual users
IMHO after https://github.com/PrivateBin/PrivateBin/pull/1754 we again need to make it a requirement in the guidelines to disclose the fact an LLM is used.

This also adds to attach screenshots for visual changes etc. (which again also ensures users actually _test_ the changes they submit)

I wrote it here and not in the wiki, because:
* this text can be read by an LLM inside the git repo, which I guess is beneficial
* the text may be shown more prominently ere IMHO
* I guess the wiki guidelines rather give a "big introduction" into how to contribute with the code base, and do not define "rules"/guidelines at all
2026-03-03 15:15:22 +01:00
33 changed files with 2026 additions and 2140 deletions

View File

@@ -36,7 +36,7 @@
} }
}, },
"features": { "features": {
"ghcr.io/devcontainers-extra/features/mocha:2": {}, "ghcr.io/devcontainers-contrib/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,7 +9,6 @@ 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,3 +6,10 @@ 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 @@
<!-- This is a template for your Pull Request. This are just some suggestions for you. You do not have to use all of them. --> <!--Please honor our guidelines as written in the CONTRIBUTING.md file. To emphasize: it is required to disclose the usage of an LLM tool. -->
<!-- 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,11 +6,12 @@ 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 --> <!-- List all the changes you have done. This section is just an example and may be removed if irrelevant. -->
* *
* *
## 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@2536c51d3d126276eb39f74d6bc9c72ac6ef30d3 uses: dawidd6/action-download-artifact@8a338493df3d275e4a7a63bcff3b8fe97e51a927
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: ip-lib 1.22.0 & polyfill-php80 1.33.0 * CHANGED: Upgrading libraries to: DOMpurify 3.3.2, 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 server ne ve ničesar o prilepljenih podatkih. Podatki so zakodirani/odkodirani %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 strežnik ne ve ničesar o prilepljenih podatkih. Podatki so šifrirani/dešifrirani %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?)": "Ne morem odkodirati prilepka: V URL-ju manjka ključ (A si uporabil 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?)": "Prilepka ni mogoče odkodirati: v URL-ju manjka ključ. Ali je bil uporabljen 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! Prosimo, 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! Prosim, 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 WebAssemblyja, ki se uporablja za stiskanje zlib. Nestisnjene dokumente lahko ustvarite, stisnjenih pa ne morete brati.", "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.",
"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.": "Do te povezave lahko dostopate samo enkrat, ne uporabljajte gumba za nazaj ali osvežitev v brskalniku.", "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«.",
"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 skrajšanja URL, ki ne kaže na naš primerek.", "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.",
"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 dokumenta zaradi manjkajoče podpore za WebAssembly.", "Error compressing document, due to missing WebAssembly support.": "Napaka pri stiskanju pripleka 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,46 +4,7 @@
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');
// initial DOM environment created by jsdom-global global.cleanup = global.jsdom();
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;
@@ -51,23 +12,14 @@ 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 ? window.PR.prettyPrint : function() {}; global.prettyPrint = window.PR.prettyPrint;
global.prettyPrintOne = window.PR ? window.PR.prettyPrintOne : function() {}; global.prettyPrintOne = window.PR.prettyPrintOne;
global.showdown = require('./showdown-2.1.0'); global.showdown = require('./showdown-2.1.0');
global.DOMPurify = require('./purify-3.3.0'); global.DOMPurify = require('./purify-3.3.2');
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'],

View File

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

View File

@@ -16,7 +16,6 @@
"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,11 +11,12 @@ 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>';
document.body.innerHTML = $('body').html(
'<div id="status"></div>'; '<div id="status"></div>'
PrivateBin.Alert.init(); );
PrivateBin.Alert.showStatus(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showStatus(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -29,13 +30,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showStatus(message); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showStatus(message);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -51,13 +53,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showStatus(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showStatus(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -72,11 +75,12 @@ 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>';
document.body.innerHTML = $('body').html(
'<div id="errormessage"></div>'; '<div id="errormessage"></div>'
PrivateBin.Alert.init(); );
PrivateBin.Alert.showWarning(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showWarning(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -91,13 +95,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showWarning(message); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showWarning(message);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -113,13 +118,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showWarning(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showWarning(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -134,11 +140,12 @@ 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>';
document.body.innerHTML = $('body').html(
'<div id="errormessage"></div>'; '<div id="errormessage"></div>'
PrivateBin.Alert.init(); );
PrivateBin.Alert.showError(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showError(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -153,13 +160,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showError(message); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showError(message);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -175,13 +183,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showError(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showError(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -197,11 +206,12 @@ 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>';
document.body.innerHTML = $('body').html(
'<div id="remainingtime" class="hidden"></div>'; '<div id="remainingtime" class="hidden"></div>'
PrivateBin.Alert.init(); );
PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -218,13 +228,14 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showRemaining(['%s' + message + '%d', string, number]); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -243,11 +254,12 @@ describe('Alert', function () {
message = defaultMessage; message = defaultMessage;
} }
const expected = '<div id="loadingindicator" class="">' + message + '</div>'; const expected = '<div id="loadingindicator" class="">' + message + '</div>';
document.body.innerHTML = $('body').html(
'<div id="loadingindicator" class="hidden">' + defaultMessage + '</div>'; '<div id="loadingindicator" class="hidden">' + defaultMessage + '</div>'
PrivateBin.Alert.init(); );
PrivateBin.Alert.showLoading(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showLoading(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -267,14 +279,15 @@ 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>';
document.body.innerHTML = $('body').html(
'<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.showLoading(message, icon); $.PrivateBin.Alert.init();
const result = document.body.innerHTML; $.PrivateBin.Alert.showLoading(message, icon);
const result = $('body').html();
return expected === result; return expected === result;
} }
); );
@@ -284,16 +297,17 @@ describe('Alert', function () {
it( it(
'hides the loading message', 'hides the loading message',
function() { function() {
document.body.innerHTML = $('body').html(
'<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'); );
PrivateBin.Alert.init(); $('body').addClass('loading');
PrivateBin.Alert.hideLoading(); $.PrivateBin.Alert.init();
assert.ok(!document.body.classList.contains('loading')); $.PrivateBin.Alert.hideLoading();
assert.ok(document.getElementById('loadingindicator').classList.contains('hidden')); assert.ok(!$('body').hasClass('loading'));
assert.ok($('#loadingindicator').hasClass('hidden'));
} }
); );
}); });
@@ -302,17 +316,18 @@ describe('Alert', function () {
it( it(
'hides all messages', 'hides all messages',
function() { function() {
document.body.innerHTML = $('body').html(
'<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.hideMessages(); $.PrivateBin.Alert.init();
assert.ok(document.getElementById('status').classList.contains('hidden')); $.PrivateBin.Alert.hideMessages();
assert.ok(document.getElementById('errormessage').classList.contains('hidden')); assert.ok($('#status').hasClass('hidden'));
assert.ok($('#errormessage').hasClass('hidden'));
} }
); );
}); });
@@ -327,15 +342,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;
} }
document.body.innerHTML = $('body').html(
'<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> ' +
@@ -348,14 +363,15 @@ 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.setCustomHandler(function(id, element) { $.PrivateBin.Alert.init();
$.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 = globalThis.cleanup(), let clean = jsdom(),
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, '');
document.body.innerHTML = ( $('body').html(
'<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,62 +51,60 @@ 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() &&
document.getElementById('attachment').classList.contains('hidden') && $('#attachment').hasClass('hidden') &&
document.getElementById('attachment').children.length === 0 && $('#attachment').children().length === 0 &&
document.getElementById('attachmenttemplate').classList.contains('hidden') && $('#attachmenttemplate').hasClass('hidden') &&
document.getElementById('attachmentPreview').classList.contains('hidden') $('#attachmentPreview').hasClass('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() &&
document.getElementById('attachment').classList.contains('hidden') && $('#attachment').hasClass('hidden') &&
document.getElementById('attachment').children.length > 0 && $('#attachment').children().length > 0 &&
document.getElementById('attachmentPreview').classList.contains('hidden') && $('#attachmentPreview').hasClass('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(
!document.getElementById('attachment').classList.contains('hidden') && !$('#attachment').hasClass('hidden') &&
document.getElementById('attachment').children.length > 0 && $('#attachment').children().length > 0 &&
(previewSupported ? !document.getElementById('attachmentPreview').classList.contains('hidden') : document.getElementById('attachmentPreview').classList.contains('hidden')) (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
); );
PrivateBin.AttachmentViewer.hideAttachment(); $.PrivateBin.AttachmentViewer.hideAttachment();
results.push( results.push(
document.getElementById('attachment').classList.contains('hidden') && $('#attachment').hasClass('hidden') &&
(previewSupported ? !document.getElementById('attachmentPreview').classList.contains('hidden') : document.getElementById('attachmentPreview').classList.contains('hidden')) (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
); );
if (previewSupported) { if (previewSupported) {
PrivateBin.AttachmentViewer.hideAttachmentPreview(); $.PrivateBin.AttachmentViewer.hideAttachmentPreview();
results.push(document.getElementById('attachmentPreview').classList.contains('hidden')); results.push($('#attachmentPreview').hasClass('hidden'));
} }
PrivateBin.AttachmentViewer.showAttachment(); $.PrivateBin.AttachmentViewer.showAttachment();
results.push( results.push(
!document.getElementById('attachment').classList.contains('hidden') && !$('#attachment').hasClass('hidden') &&
(previewSupported ? !document.getElementById('attachmentPreview').classList.contains('hidden') : document.getElementById('attachmentPreview').classList.contains('hidden')) (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
); );
let element = document.createElement('div'); let element = $('<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) {
const tempTA = document.createElement('textarea'); result = $('<textarea>').text((prefix + filename + postfix)).text();
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']
} }
@@ -114,18 +112,18 @@ describe('AttachmentViewer', function () {
} }
if (filename.length) { if (filename.length) {
results.push( results.push(
element.querySelector('a').href === data && element.find('a')[0].href === data &&
element.querySelector('a').getAttribute('download') === filename && element.find('a')[0].getAttribute('download') === filename &&
element.querySelector('a').textContent === result element.find('a')[0].text === result
); );
} else { } else {
results.push(element.querySelector('a').href === data); results.push(element.find('a')[0].href === data);
} }
PrivateBin.AttachmentViewer.removeAttachment(); $.PrivateBin.AttachmentViewer.removeAttachment();
results.push( results.push(
document.getElementById('attachment').classList.contains('hidden') && $('#attachment').hasClass('hidden') &&
document.getElementById('attachment').children.length === 0 && $('#attachment').children().length === 0 &&
document.getElementById('attachmentPreview').classList.contains('hidden') $('#attachmentPreview').hasClass('hidden')
); );
clean(); clean();
return results.every(element => element); return results.every(element => element);
@@ -135,8 +133,8 @@ describe('AttachmentViewer', function () {
it( it(
'sanitizes file names in attachments', 'sanitizes file names in attachments',
function() { function() {
const clean = globalThis.cleanup(); const clean = jsdom();
document.body.innerHTML = ( $('body').html(
'<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">' +
@@ -156,8 +154,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 = [
@@ -165,8 +163,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(!document.body.innerHTML.includes(filename)); assert.ok(!$('body').html().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 = globalThis.cleanup( const clean = jsdom(
'<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 = globalThis.cleanup( clean = jsdom(
'<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 = globalThis.cleanup( const clean = jsdom(
'<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 = globalThis.cleanup(); var clean = jsdom();
common.enableClipboard(); common.enableClipboard();
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">' + '+++</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();
document.getElementById('prettyMessageCopyBtn').click(); $('#prettyMessageCopyBtn').trigger('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 = globalThis.cleanup(); var clean = jsdom();
common.enableClipboard(); common.enableClipboard();
document.body.innerHTML = ( $('body').html(
'<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();
document.body.dispatchEvent(new Event('copy')); $('body').trigger('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 = globalThis.cleanup(); var clean = jsdom();
common.enableClipboard(); common.enableClipboard();
document.body.innerHTML = '<button id="copyLink"></button>'; $('body').html('<button id="copyLink"></button>');
PrivateBin.CopyToClipboard.init(); $.PrivateBin.CopyToClipboard.init();
PrivateBin.CopyToClipboard.setUrl(text); $.PrivateBin.CopyToClipboard.setUrl(text);
document.getElementById('copyLink').click(); $('#copyLink').trigger('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 = globalThis.cleanup(); var clean = jsdom();
document.body.innerHTML = '<small id="copyShortcutHintText"></small>'; $('body').html('<small id="copyShortcutHintText"></small>');
PrivateBin.CopyToClipboard.init(); $.PrivateBin.CopyToClipboard.init();
PrivateBin.CopyToClipboard.showKeyboardShortcutHint(); $.PrivateBin.CopyToClipboard.showKeyboardShortcutHint();
const keyboardShortcutHint = document.getElementById('copyShortcutHintText').textContent; const keyboardShortcutHint = $('#copyShortcutHintText').text();
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 = globalThis.cleanup(); var clean = jsdom();
document.body.innerHTML = '<small id="copyShortcutHintText">' + text + '</small>'; $('body').html('<small id="copyShortcutHintText">' + text + '</small>');
PrivateBin.CopyToClipboard.init(); $.PrivateBin.CopyToClipboard.init();
PrivateBin.CopyToClipboard.hideKeyboardShortcutHint(); $.PrivateBin.CopyToClipboard.hideKeyboardShortcutHint();
const keyboardShortcutHint = document.getElementById('copyShortcutHintText').textContent; const keyboardShortcutHint = $('#copyShortcutHintText').text();
clean(); clean();

View File

@@ -1,6 +1,5 @@
'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 () {
@@ -16,9 +15,9 @@ describe('CryptTool', function () {
'string', 'string',
'string', 'string',
async function (key, password, message) { async function (key, password, message) {
const clean = globalThis.cleanup(); const clean = jsdom();
// 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
@@ -26,10 +25,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();
@@ -43,19 +42,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 = globalThis.cleanup(); clean = jsdom();
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();
@@ -92,17 +91,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 = globalThis.cleanup(); const clean = jsdom();
// 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();
@@ -124,12 +123,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 = globalThis.cleanup(); const clean = jsdom();
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,91 +26,81 @@ 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 = globalThis.cleanup(), var clean = jsdom(),
results = []; results = [];
document.body.innerHTML = ( $('body').html(
`<div id="discussion"> '<div id="discussion"><h4>Discussion</h4>' +
<h4>Discussion</h4> '<div id="commentcontainer"></div></div><div id="templates">' +
<div id="commentcontainer"></div> '<article id="commenttemplate" class="comment">' +
</div> '<div class="commentmeta"><span class="nickname">name</span>' +
<div id="templates"> '<span class="commentdate">0000-00-00</span></div>' +
<article id="commenttemplate" class="comment"> '<div class="commentdata">c</div>' +
<div class="commentmeta"> '<button class="btn btn-default btn-sm">Reply</button>' +
<span class="nickname">name</span> '</article><p id="commenttailtemplate" class="comment">' +
<span class="commentdate">0000-00-00</span> '<button class="btn btn-default btn-sm">Add comment</button>' +
</div> '</p><div id="replytemplate" class="reply hidden">' +
<div class="commentdata">c</div> '<input type="text" id="nickname" class="form-control" ' +
<button class="btn btn-default btn-sm">Reply</button> 'title="Optional nickname…" placeholder="Optional ' +
</article> 'nickname…" /><textarea id="replymessage" ' +
<p id="commenttailtemplate" class="comment"> 'class="replymessage form-control" cols="80" rows="7">' +
<button class="btn btn-default btn-sm">Add comment</button> '</textarea><br /><div id="replystatus" role="alert" ' +
</p> 'class="statusmessage hidden alert"><span class="glyphicon" ' +
<div id="replytemplate" class="reply hidden"> 'aria-hidden="true"></span> </div><button id="replybutton" ' +
<input type="text" id="nickname" class="form-control" title="Optional nickname…" 'class="btn btn-default btn-sm">Post comment</button></div></div>'
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(
!document.getElementById('discussion').classList.contains('hidden') !$('#discussion').hasClass('hidden')
); );
PrivateBin.DiscussionViewer.prepareNewDiscussion(); $.PrivateBin.DiscussionViewer.prepareNewDiscussion();
results.push( results.push(
document.getElementById('discussion').classList.contains('hidden') $('#discussion').hasClass('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(
document.getElementById('discussion').classList.contains('hidden') $('#discussion').hasClass('hidden')
); );
PrivateBin.DiscussionViewer.finishDiscussion(); $.PrivateBin.DiscussionViewer.finishDiscussion();
results.push( results.push(
!document.getElementById('discussion').classList.contains('hidden') && !$('#discussion').hasClass('hidden') &&
comments.length + 1 >= document.getElementById('commentcontainer').children.length comments.length + 1 >= $('#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(
document.getElementById('comment_' + comments[commentKey].id).classList.contains('highlight') $('#comment_' + comments[commentKey].id).hasClass('highlight')
); );
} }
document.getElementById('commentcontainer').querySelector('button').click(); $('#commentcontainer').find('button')[0].click();
results.push( results.push(
!document.getElementById('reply').classList.contains('hidden') !$('#reply').hasClass('hidden')
); );
document.querySelector('#reply #nickname').value = nickname; $('#reply #nickname').val(nickname);
document.querySelector('#reply #replymessage').value = message; $('#reply #replymessage').val(message);
PrivateBin.DiscussionViewer.getReplyCommentId(); $.PrivateBin.DiscussionViewer.getReplyCommentId();
results.push( results.push(
PrivateBin.DiscussionViewer.getReplyNickname() === document.querySelector('#reply #nickname').value && $.PrivateBin.DiscussionViewer.getReplyNickname() === $('#reply #nickname').val() &&
PrivateBin.DiscussionViewer.getReplyMessage() === document.querySelector('#reply #replymessage').value $.PrivateBin.DiscussionViewer.getReplyMessage() === $('#reply #replymessage').val()
); );
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.classList.contains('alert-danger') && notificationResult.hasClass('alert-danger') &&
!notificationResult.classList.contains('alert-info') !notificationResult.hasClass('alert-info')
) : ( ) : (
!notificationResult.classList.contains('alert-danger') && !notificationResult.hasClass('alert-danger') &&
notificationResult.classList.contains('alert-info') notificationResult.hasClass('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 = globalThis.cleanup(), var clean = jsdom(),
results = []; results = [];
document.body.innerHTML = ( $('body').html(
'<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(
document.getElementById('editorTabs').classList.contains('hidden') && $('#editorTabs').hasClass('hidden') &&
document.getElementById('message').classList.contains('hidden') $('#message').hasClass('hidden')
); );
PrivateBin.Editor.show(); $.PrivateBin.Editor.show();
results.push( results.push(
!document.getElementById('editorTabs').classList.contains('hidden') && !$('#editorTabs').hasClass('hidden') &&
!document.getElementById('message').classList.contains('hidden') !$('#message').hasClass('hidden')
); );
PrivateBin.Editor.hide(); $.PrivateBin.Editor.hide();
results.push( results.push(
document.getElementById('editorTabs').classList.contains('hidden') && $('#editorTabs').hasClass('hidden') &&
document.getElementById('message').classList.contains('hidden') $('#message').hasClass('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() === document.getElementById('message').value $.PrivateBin.Editor.getText() === $('#message').val()
); );
PrivateBin.Editor.setText(); $.PrivateBin.Editor.setText();
results.push( results.push(
!PrivateBin.Editor.isPreview() && !$.PrivateBin.Editor.isPreview() &&
!document.getElementById('message').classList.contains('hidden') !$('#message').hasClass('hidden')
); );
document.getElementById('messagepreview').click(); $('#messagepreview').trigger('click');
results.push( results.push(
PrivateBin.Editor.isPreview() && $.PrivateBin.Editor.isPreview() &&
document.getElementById('message').classList.contains('hidden') $('#message').hasClass('hidden')
); );
document.getElementById('messageedit').click(); $('#messageedit').trigger('click');
results.push( results.push(
!PrivateBin.Editor.isPreview() && !$.PrivateBin.Editor.isPreview() &&
!document.getElementById('message').classList.contains('hidden') !$('#message').hasClass('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 = globalThis.cleanup(html); clean = jsdom(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,15 +73,9 @@ 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 = globalThis.cleanup(); cleanup = jsdom();
}); });
jsc.property( jsc.property(
@@ -90,12 +84,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 = globalThis.cleanup(); let clean = jsdom();
document.body.innerHTML = '<div id="foo"></div>'; $('body').html('<div id="foo"></div>');
let e = document.getElementById('foo'); let e = $('#foo');
e.textContent = content; e.text(content);
PrivateBin.Helper.urls2links(e); $.PrivateBin.Helper.urls2links(e);
let result = e.textContent; let result = e.text();
clean(); clean();
return content === result; return content === result;
} }
@@ -113,9 +107,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 = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div id="foo"></div>'; $('body').html('<div id="foo"></div>');
let e = document.getElementById('foo'); let e = $('#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 (
@@ -126,14 +120,12 @@ describe('Helper', function () {
urlString = common.urlToString(url); urlString = common.urlToString(url);
postfix = ''; postfix = '';
} }
e.textContent = prefix + urlString + postfix; e.text(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();
urlString = getTextAsRenderedHtml(urlString); const expected = $('<div />').text(prefix).html() + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + $('<div />').text(postfix).html();
const expected = getTextAsRenderedHtml(prefix) + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + getTextAsRenderedHtml(postfix);
return expected === result; return expected === result;
} }
); );
@@ -148,15 +140,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 = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div id="foo"></div>'; $('body').html('<div id="foo"></div>');
let e = document.getElementById('foo'); let e = $('#foo');
e.textContent = prefix + url + postfix; e.text(prefix + url + postfix);
PrivateBin.Helper.urls2links(e); $.PrivateBin.Helper.urls2links(e);
let result = e.innerHTML; let result = e.html();
clean(); clean();
url = getTextAsRenderedHtml(url); url = $('<div />').text(url).html();
return getTextAsRenderedHtml(prefix) + '<a href="' + url + '" target="_blank" rel="nofollow noopener noreferrer">' + url + '</a>' + getTextAsRenderedHtml(postfix) === result; return $('<div />').text(prefix).html() + '<a href="' + url + '" target="_blank" rel="nofollow noopener noreferrer">' + url + '</a>' + $('<div />').text(postfix).html() === result;
} }
); );
}); });
@@ -173,7 +165,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(
@@ -186,7 +178,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(
@@ -199,7 +191,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(
@@ -215,7 +207,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(
@@ -231,7 +223,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);
} }
); );
}); });
@@ -249,7 +241,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 = globalThis.cleanup(); const clean = jsdom();
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('');
@@ -260,8 +252,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;
} }
@@ -279,10 +271,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 = globalThis.cleanup('', {url: fullUrl}), clean = jsdom('', {url: fullUrl}),
result = PrivateBin.Helper.baseUri(); result = $.PrivateBin.Helper.baseUri();
clean(); clean();
return expected === result; return expected === result;
} }
@@ -291,14 +283,14 @@ describe('Helper', function () {
describe('htmlEntities', function () { describe('htmlEntities', function () {
before(function () { before(function () {
cleanup = globalThis.cleanup(); cleanup = jsdom();
}); });
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)));
} }
); );
@@ -306,43 +298,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';
}); });
}); });
@@ -350,12 +342,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,25 +99,22 @@ 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 tempDiv = document.createElement('textarea'); const translation = $('<textarea>').text((prefix + params[0] + postfix)).text();
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 = globalThis.cleanup(); let clean = jsdom();
document.body.innerHTML = '<div id="i18n"></div>'; $('body').html('<div id="i18n"></div>');
const i18nElement = document.getElementById('i18n'); args.unshift($('#i18n'));
args.unshift(i18nElement); $.PrivateBin.I18n.translate.apply(this, args);
PrivateBin.I18n.translate.apply(this, args); const result = $('#i18n').text();
const result = i18nElement.textContent; $.PrivateBin.I18n.reset();
PrivateBin.I18n.reset();
clean(); clean();
clean = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div id="i18n"></div>'; $('body').html('<div id="i18n"></div>');
args[0] = document.getElementById('i18n'); args[0] = $('#i18n');
PrivateBin.I18n._.apply(this, args); $.PrivateBin.I18n._.apply(this, args);
const alias = document.getElementById('i18n').textContent; const alias = $('#i18n').text();
PrivateBin.I18n.reset(); $.PrivateBin.I18n.reset();
clean(); clean();
return translation === result && translation === alias; return translation === result && translation === alias;
} }
@@ -140,20 +137,19 @@ 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 = globalThis.cleanup(); let clean = jsdom();
document.body.innerHTML = '<div id="i18n"></div>'; $('body').html('<div id="i18n"></div>');
const i18nElement2 = document.getElementById('i18n'); args.unshift($('#i18n'));
args.unshift(i18nElement2); $.PrivateBin.I18n.translate.apply(this, args);
PrivateBin.I18n.translate.apply(this, args); const result = $('#i18n').html();
const result = i18nElement2.innerHTML; $.PrivateBin.I18n.reset();
PrivateBin.I18n.reset();
clean(); clean();
clean = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div id="i18n"></div>'; $('body').html('<div id="i18n"></div>');
args[0] = document.getElementById('i18n'); args[0] = $('#i18n');
PrivateBin.I18n._.apply(this, args); $.PrivateBin.I18n._.apply(this, args);
const alias = document.getElementById('i18n').innerHTML; const alias = $('#i18n').html();
PrivateBin.I18n.reset(); $.PrivateBin.I18n.reset();
clean(); clean();
return translation === result && translation === alias; return translation === result && translation === alias;
} }
@@ -162,7 +158,7 @@ describe('I18n', function () {
describe('getPluralForm', function () { describe('getPluralForm', function () {
before(function () { before(function () {
PrivateBin.I18n.reset(); $.PrivateBin.I18n.reset();
}); });
jsc.property( jsc.property(
@@ -170,8 +166,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;
} }
@@ -183,7 +179,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(
@@ -191,18 +187,18 @@ describe('I18n', function () {
common.jscSupportedLanguages(), common.jscSupportedLanguages(),
function(language) { function(language) {
// cleanup // cleanup
var clean = globalThis.cleanup('', {cookie: ['lang=en']}); var clean = jsdom('', {cookie: ['lang=en']});
PrivateBin.I18n.reset('en'); $.PrivateBin.I18n.reset('en');
PrivateBin.I18n.loadTranslations(); $.PrivateBin.I18n.loadTranslations();
clean(); clean();
// mock // mock
clean = globalThis.cleanup('', {cookie: ['lang=' + language]}); clean = jsdom('', {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;
} }
@@ -211,7 +207,7 @@ describe('I18n', function () {
jsc.property( jsc.property(
'should default to en', 'should default to en',
function() { function() {
var clean = globalThis.cleanup('', {url: 'https://privatebin.net/'}); var clean = jsdom('', {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
@@ -222,10 +218,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 = globalThis.cleanup(); cleanup = jsdom();
}); });
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>';
document.body.innerHTML = contents; $('body').html(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>';
document.body.innerHTML = contents; $('body').html(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 = globalThis.cleanup('', {url: common.urlToString(url)}); clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}); const clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}); const clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}), const clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}), const clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}), const clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}), let clean = jsdom('', {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,18 +226,15 @@ describe('Model', function () {
element = 'p'; element = 'p';
} }
document.body.innerHTML = ( $('body').html(
'<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 + '>',
templateEl = PrivateBin.Model.getTemplate(id), result = $.PrivateBin.Model.getTemplate(id).wrap('<p/>').parent().html();
wrapper = document.createElement('p'); $.PrivateBin.Model.reset();
wrapper.appendChild(templateEl.cloneNode(true));
var result = wrapper.innerHTML;
PrivateBin.Model.reset();
return template === result; return template === result;
} }
); );

View File

@@ -22,39 +22,21 @@ 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 (jsc)', 'creates a notification after a successful document upload',
common.jscUrl(), common.jscUrl(),
common.jscUrl(false), common.jscUrl(false),
function (url1, url2) { function (url1, url2) {
// sometimes the generator returns incomplete objects, bail out
if (!url1 || !url1.address || !url2 || !url2.address) {
return true;
}
const expected1 = common.urlToString(url1).replace(/&(gt|lt)$/, '&$1a'), const expected1 = common.urlToString(url1).replace(/&(gt|lt)$/, '&$1a'),
expected2 = common.urlToString(url2).replace(/&(gt|lt)$/, '&$1a'); expected2 = common.urlToString(url2).replace(/&(gt|lt)$/, '&$1a'),
cleanup(); clean = jsdom();
document.body.innerHTML = '<a href="#" id="deletelink"><span></span></a><div id="pastelink"></div><div id="pastesuccess"></div>'; $('body').html('<a href="#" id="deletelink"><span></span></a><div id="pastelink"></div>');
PrivateBin.PasteStatus.init(); $.PrivateBin.PasteStatus.init();
PrivateBin.PasteStatus.createPasteNotification(expected1, expected2); $.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
const result1 = $('#pasteurl')[0].href,
assert.ok(!document.getElementById('pastesuccess').classList.contains('hidden')); result2 = $('#deletelink')[0].href;
clean();
const result2 = document.getElementById('deletelink').href; return result1 === expected1 && result2 === expected2;
return document.getElementById('pasteurl').href === expected1 && result2 === expected2;
} }
); );
}); });
@@ -77,14 +59,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 = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div><div id="pastelink"></div></div>'; $('body').html('<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 = document.getElementById('pasteurl').href; const result = $('#pasteurl')[0].href;
clean(); clean();
return result.endsWith(expected) && ( return result.endsWith(expected) && (
@@ -116,14 +98,14 @@ describe('PasteStatus', function () {
shorturl: shortUrlString, shorturl: shortUrlString,
statusCode: 200 statusCode: 200
}, },
clean = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div><div id="pastelink"></div></div>'; $('body').html('<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 = document.getElementById('pasteurl').href; const result = $('#pasteurl')[0].href;
clean(); clean();
return result === shortUrlString; return result === shortUrlString;
@@ -143,14 +125,14 @@ describe('PasteStatus', function () {
' <message>success</message>\n' + ' <message>success</message>\n' +
' <statusCode>200</statusCode>\n' + ' <statusCode>200</statusCode>\n' +
'</result>', '</result>',
clean = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div><div id="pastelink"></div></div>'; $('body').html('<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 = document.getElementById('pasteurl').href; const result = $('#pasteurl')[0].href;
clean(); clean();
return result === shortUrlString; return result === shortUrlString;
@@ -175,14 +157,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 = globalThis.cleanup(); clean = jsdom();
document.body.innerHTML = '<div><div id="pastelink"></div></div>'; $('body').html('<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 = document.getElementById('pasteurl').href; const result = $('#pasteurl')[0].href;
clean(); clean();
return result === shortUrlString; return result === shortUrlString;
@@ -199,11 +181,11 @@ describe('PasteStatus', function () {
'nat', 'nat',
common.jscUrl(), common.jscUrl(),
function (burnafterreading, remainingTime, url) { function (burnafterreading, remainingTime, url) {
let clean = globalThis.cleanup('', {url: common.urlToString(url)}), let clean = jsdom('', {url: common.urlToString(url)}),
result; result;
document.body.innerHTML = '<div id="remainingtime" class="hidden"></div>'; $('body').html('<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': {
@@ -211,14 +193,14 @@ describe('PasteStatus', function () {
} }
})); }));
if (burnafterreading) { if (burnafterreading) {
result = document.getElementById('remainingtime').classList.contains('foryoureyesonly') && result = $('#remainingtime').hasClass('foryoureyesonly') &&
!document.getElementById('remainingtime').classList.contains('hidden'); !$('#remainingtime').hasClass('hidden');
} else if (remainingTime) { } else if (remainingTime) {
result =!document.getElementById('remainingtime').classList.contains('foryoureyesonly') && result =!$('#remainingtime').hasClass('foryoureyesonly') &&
!document.getElementById('remainingtime').classList.contains('hidden'); !$('#remainingtime').hasClass('hidden');
} else { } else {
result = document.getElementById('remainingtime').classList.contains('hidden') && result = $('#remainingtime').hasClass('hidden') &&
!document.getElementById('remainingtime').classList.contains('foryoureyesonly'); !$('#remainingtime').hasClass('foryoureyesonly');
} }
clean(); clean();
return result; return result;
@@ -230,13 +212,13 @@ describe('PasteStatus', function () {
it( it(
'hides all messages', 'hides all messages',
function() { function() {
document.body.innerHTML = ( $('body').html(
'<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(document.getElementById('remainingtime').classList.contains('hidden')); assert.ok($('#remainingtime').hasClass('hidden'));
assert.ok(document.getElementById('pastesuccess').classList.contains('hidden')); assert.ok($('#pastesuccess').hasClass('hidden'));
} }
); );
}); });

View File

@@ -5,75 +5,60 @@ 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) {
cleanup(); var clean = jsdom(),
var results = []; results = [];
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(''); $.PrivateBin.PasteViewer.setText('');
results.push( results.push(
document.getElementById('placeholder').classList.contains('hidden') && $('#placeholder').hasClass('hidden') &&
document.getElementById('prettymessage').classList.contains('hidden') && $('#prettymessage').hasClass('hidden') &&
document.getElementById('plaintext').classList.contains('hidden') && $('#plaintext').hasClass('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(
!document.getElementById('placeholder').classList.contains('hidden') && !$('#placeholder').hasClass('hidden') &&
document.getElementById('prettymessage').classList.contains('hidden') && $('#prettymessage').hasClass('hidden') &&
document.getElementById('plaintext').classList.contains('hidden') $('#plaintext').hasClass('hidden')
); );
PrivateBin.PasteViewer.hide(); $.PrivateBin.PasteViewer.hide();
results.push( results.push(
document.getElementById('placeholder').classList.contains('hidden') && $('#placeholder').hasClass('hidden') &&
document.getElementById('prettymessage').classList.contains('hidden') && $('#prettymessage').hasClass('hidden') &&
document.getElementById('plaintext').classList.contains('hidden') $('#plaintext').hasClass('hidden')
); );
PrivateBin.PasteViewer.setText(text); $.PrivateBin.PasteViewer.setText(text);
PrivateBin.PasteViewer.run(); $.PrivateBin.PasteViewer.run();
results.push( results.push(
document.getElementById('placeholder').classList.contains('hidden') && $('#placeholder').hasClass('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(
document.getElementById('prettymessage').classList.contains('hidden') && $('#prettymessage').hasClass('hidden') &&
!document.getElementById('plaintext').classList.contains('hidden') !$('#plaintext').hasClass('hidden')
); );
} else { } else {
results.push( results.push(
!document.getElementById('prettymessage').classList.contains('hidden') && !$('#prettymessage').hasClass('hidden') &&
document.getElementById('plaintext').classList.contains('hidden') $('#plaintext').hasClass('hidden')
); );
} }
cleanup(); clean();
return results.every(element => element); return results.every(element => element);
} }
); );
@@ -112,19 +97,20 @@ describe('PasteViewer', function () {
]), ]),
'string', 'string',
function (format, prefix, xss, suffix) { function (format, prefix, xss, suffix) {
var text = prefix + xss + suffix; var clean = jsdom(),
document.body.innerHTML = ( text = prefix + xss + suffix;
$('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 = document.body.innerHTML.indexOf(xss) === -1; var result = $('body').html().indexOf(xss) === -1;
cleanup(); clean();
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 = globalThis.cleanup('', {url: 'ftp://example.com/?0000000000000000'}); /* const clean = */ jsdom('', {url: 'ftp://example.com/?0000000000000000'});
document.body.innerHTML = ( $('body').html(
'<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,10 +20,20 @@ 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>'
); );
const passwordInput = document.getElementById('passworddecrypt'); $.PrivateBin.Model.reset();
passwordInput.value = password; $.PrivateBin.Model.init();
const result = passwordInput.value; // eslint-disable-next-line global-require
clean(); global.bootstrap = require('../bootstrap-5.3.8');
$.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 = globalThis.cleanup(); let clean = jsdom();
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,20 +1,6 @@
'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 () {
@@ -25,39 +11,45 @@ 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 = [];
document.documentElement.innerHTML = $('body').html(
`<nav class="navbar navbar-inverse navbar-static-top"> '<nav class="navbar navbar-inverse navbar-static-top">' +
<div id="navbar" class="navbar-collapse collapse"> '<div id="navbar" class="navbar-collapse collapse"><ul ' +
<ul class="nav navbar-nav"><li><button id="newbutton" type="button" class="hidden btn btn-warning navbar-btn"> 'class="nav navbar-nav"><li><button id="newbutton" ' +
<span class="glyphicon glyphicon-file" aria-hidden="true"> 'type="button" class="hidden btn btn-warning navbar-btn">' +
</span> New</button><button id="clonebutton" type="button" class="hidden btn btn-warning navbar-btn"> '<span class="glyphicon glyphicon-file" aria-hidden="true">' +
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> Clone</button> '</span> New</button><button id="clonebutton" type="button"' +
<button id="rawtextbutton" type="button" class="hidden btn btn-warning navbar-btn"> ' class="hidden btn btn-warning navbar-btn">' +
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> Raw text</button> '<span class="glyphicon glyphicon-duplicate" ' +
<button id="downloadtextbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn"></button> 'aria-hidden="true"></span> Clone</button><button ' +
<button id="qrcodelink" type="button" data-toggle="modal" data-target="#qrcodemodal" class="hidden btn btn-warning navbar-btn"/> 'id="rawtextbutton" type="button" class="hidden btn ' +
<span class="glyphicon glyphicon-qrcode" aria-hidden="true"></span> QR code</button></li></ul></div> 'btn-warning navbar-btn"><span class="glyphicon ' +
</nav>`; 'glyphicon-text-background" aria-hidden="true"></span> ' +
PrivateBin.TopNav.init(); 'Raw text</button><button id="qrcodelink" type="button" ' +
results.push( 'data-toggle="modal" data-target="#qrcodemodal" ' +
query('#newbutton').classList.contains('hidden') && 'class="hidden btn btn-warning navbar-btn"><span ' +
query('#clonebutton').classList.contains('hidden') && 'class="glyphicon glyphicon-qrcode" aria-hidden="true">' +
query('#rawtextbutton').classList.contains('hidden') && '</span> QR code</button></li></ul></div></nav>'
query('#qrcodelink').classList.contains('hidden')
); );
PrivateBin.TopNav.showViewButtons(); $.PrivateBin.TopNav.init();
results.push( results.push(
!query('#newbutton').classList.contains('hidden') && $('#newbutton').hasClass('hidden') &&
!query('#clonebutton').classList.contains('hidden') && $('#clonebutton').hasClass('hidden') &&
!query('#rawtextbutton').classList.contains('hidden') && $('#rawtextbutton').hasClass('hidden') &&
!query('#qrcodelink').classList.contains('hidden') $('#qrcodelink').hasClass('hidden')
); );
PrivateBin.TopNav.hideViewButtons(); $.PrivateBin.TopNav.showViewButtons();
results.push( results.push(
query('#newbutton').classList.contains('hidden') && !$('#newbutton').hasClass('hidden') &&
query('#clonebutton').classList.contains('hidden') && !$('#clonebutton').hasClass('hidden') &&
query('#rawtextbutton').classList.contains('hidden') && !$('#rawtextbutton').hasClass('hidden') &&
query('#qrcodelink').classList.contains('hidden') !$('#qrcodelink').hasClass('hidden')
);
$.PrivateBin.TopNav.hideViewButtons();
results.push(
$('#newbutton').hasClass('hidden') &&
$('#clonebutton').hasClass('hidden') &&
$('#rawtextbutton').hasClass('hidden') &&
$('#qrcodelink').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -78,7 +70,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 = [];
document.documentElement.innerHTML = $('body').html(
'<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>' +
@@ -89,39 +81,40 @@ 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();
results.push(
query('#sendbutton').classList.contains('hidden') &&
query('#expiration').classList.contains('hidden') &&
query('#formatter').classList.contains('hidden') &&
query('#burnafterreadingoption').classList.contains('hidden') &&
query('#opendiscussionoption').classList.contains('hidden') &&
query('#newbutton').classList.contains('hidden') &&
query('#password').classList.contains('hidden') &&
query('#attach').classList.contains('hidden')
); );
PrivateBin.TopNav.showCreateButtons(); $.PrivateBin.TopNav.init();
results.push( results.push(
!query('#sendbutton').classList.contains('hidden') && $('#sendbutton').hasClass('hidden') &&
!query('#expiration').classList.contains('hidden') && $('#expiration').hasClass('hidden') &&
!query('#formatter').classList.contains('hidden') && $('#formatter').hasClass('hidden') &&
!query('#burnafterreadingoption').classList.contains('hidden') && $('#burnafterreadingoption').hasClass('hidden') &&
!query('#opendiscussionoption').classList.contains('hidden') && $('#opendiscussionoption').hasClass('hidden') &&
!query('#newbutton').classList.contains('hidden') && $('#newbutton').hasClass('hidden') &&
!query('#password').classList.contains('hidden') && $('#password').hasClass('hidden') &&
!query('#attach').classList.contains('hidden') $('#attach').hasClass('hidden')
); );
PrivateBin.TopNav.hideCreateButtons(); $.PrivateBin.TopNav.showCreateButtons();
results.push( results.push(
query('#sendbutton').classList.contains('hidden') && !$('#sendbutton').hasClass('hidden') &&
query('#expiration').classList.contains('hidden') && !$('#expiration').hasClass('hidden') &&
query('#formatter').classList.contains('hidden') && !$('#formatter').hasClass('hidden') &&
query('#burnafterreadingoption').classList.contains('hidden') && !$('#burnafterreadingoption').hasClass('hidden') &&
query('#opendiscussionoption').classList.contains('hidden') && !$('#opendiscussionoption').hasClass('hidden') &&
query('#newbutton').classList.contains('hidden') && !$('#newbutton').hasClass('hidden') &&
query('#password').classList.contains('hidden') && !$('#password').hasClass('hidden') &&
query('#attach').classList.contains('hidden') !$('#attach').hasClass('hidden')
);
$.PrivateBin.TopNav.hideCreateButtons();
results.push(
$('#sendbutton').hasClass('hidden') &&
$('#expiration').hasClass('hidden') &&
$('#formatter').hasClass('hidden') &&
$('#burnafterreadingoption').hasClass('hidden') &&
$('#opendiscussionoption').hasClass('hidden') &&
$('#newbutton').hasClass('hidden') &&
$('#password').hasClass('hidden') &&
$('#attach').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -142,16 +135,17 @@ describe('TopNav', function () {
'displays the button for creating a document', 'displays the button for creating a document',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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();
results.push(
query('#newbutton').classList.contains('hidden')
); );
PrivateBin.TopNav.showNewPasteButton(); $.PrivateBin.TopNav.init();
results.push( results.push(
!query('#newbutton').classList.contains('hidden') $('#newbutton').hasClass('hidden')
);
$.PrivateBin.TopNav.showNewPasteButton();
results.push(
!$('#newbutton').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -172,18 +166,19 @@ describe('TopNav', function () {
'hides the button for cloning a document', 'hides the button for cloning a document',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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();
results.push(
!query('#clonebutton').classList.contains('hidden')
); );
PrivateBin.TopNav.hideCloneButton(); $.PrivateBin.TopNav.init();
results.push( results.push(
query('#clonebutton').classList.contains('hidden') !$('#clonebutton').hasClass('hidden')
);
$.PrivateBin.TopNav.hideCloneButton();
results.push(
$('#clonebutton').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -204,19 +199,20 @@ describe('TopNav', function () {
'hides the raw text button', 'hides the raw text button',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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();
results.push(
!query('#rawtextbutton').classList.contains('hidden')
); );
PrivateBin.TopNav.hideRawButton(); $.PrivateBin.TopNav.init();
results.push( results.push(
query('#rawtextbutton').classList.contains('hidden') !$('#rawtextbutton').hasClass('hidden')
);
$.PrivateBin.TopNav.hideRawButton();
results.push(
$('#rawtextbutton').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -237,7 +233,7 @@ describe('TopNav', function () {
'hides the file attachment selection button', 'hides the file attachment selection button',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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" ' +
@@ -246,14 +242,15 @@ 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();
results.push(
!query('#filewrap').classList.contains('hidden')
); );
PrivateBin.TopNav.hideFileSelector(); $.PrivateBin.TopNav.init();
results.push( results.push(
query('#filewrap').classList.contains('hidden') !$('#filewrap').hasClass('hidden')
);
$.PrivateBin.TopNav.hideFileSelector();
results.push(
$('#filewrap').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -274,7 +271,7 @@ describe('TopNav', function () {
'display the custom file attachment', 'display the custom file attachment',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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" ' +
@@ -283,14 +280,15 @@ 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();
results.push(
query('#customattachment').classList.contains('hidden')
); );
PrivateBin.TopNav.showCustomAttachment(); $.PrivateBin.TopNav.init();
results.push( results.push(
!query('#customattachment').classList.contains('hidden') $('#customattachment').hasClass('hidden')
);
$.PrivateBin.TopNav.showCustomAttachment();
results.push(
!$('#customattachment').hasClass('hidden')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -310,8 +308,9 @@ 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 = [];
document.documentElement.innerHTML = $('body').html(
'<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' +
@@ -319,33 +318,34 @@ 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();
results.push(
query('.navbar-toggle').classList.contains('collapsed') &&
query('#navbar').getAttribute('aria-expanded') !== 'true'
); );
PrivateBin.TopNav.collapseBar(); $.PrivateBin.TopNav.init();
results.push( results.push(
query('.navbar-toggle').classList.contains('collapsed') && $('.navbar-toggle').hasClass('collapsed') &&
query('#navbar').getAttribute('aria-expanded') !== 'true' $('#navbar').attr('aria-expanded') !== 'true'
);
$.PrivateBin.TopNav.collapseBar();
results.push(
$('.navbar-toggle').hasClass('collapsed') &&
$('#navbar').attr('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
const toggleBtn = document.querySelector('.navbar-toggle'); $('.navbar-toggle').trigger('click');
toggleBtn.dispatchEvent(new MouseEvent('click'));
results.push( results.push(
!toggleBtn.classList.contains('collapsed') && !$('.navbar-toggle').hasClass('collapsed') &&
document.getElementById('navbar').getAttribute('aria-expanded') == 'true' $('#navbar').attr('aria-expanded') == 'true'
); );
PrivateBin.TopNav.collapseBar(); $.PrivateBin.TopNav.collapseBar();
results.push( results.push(
document.querySelector('.navbar-toggle').classList.contains('collapsed') && $('.navbar-toggle').hasClass('collapsed') &&
document.getElementById('navbar').getAttribute('aria-expanded') == 'false' $('#navbar').attr('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,35 +364,36 @@ describe('TopNav', function () {
'reset inputs to defaults (options off)', 'reset inputs to defaults (options off)',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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()
); );
query('#burnafterreading').checked = true; $('#burnafterreading').attr('checked', 'checked');
query('#opendiscussion').checked = true; $('#opendiscussion').attr('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);
@@ -407,35 +408,35 @@ describe('TopNav', function () {
'reset inputs to defaults (burnafterreading on)', 'reset inputs to defaults (burnafterreading on)',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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()
); );
query('#burnafterreading').checked = false; $('#burnafterreading').removeAttr('checked');
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);
@@ -450,37 +451,36 @@ describe('TopNav', function () {
'reset inputs to defaults (opendiscussion on)', 'reset inputs to defaults (opendiscussion on)',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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()
); );
query('#opendiscussion').checked = false; $('#opendiscussion').removeAttr('checked');
query('#opendiscussion').removeAttribute('checked'); $('#burnafterreading').prop('checked', true);
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,12 +500,13 @@ describe('TopNav', function () {
it( it(
'returns the currently selected expiration date', 'returns the currently selected expiration date',
function () { function () {
document.documentElement.innerHTML = $('body').html(
'<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(); );
assert.strictEqual(PrivateBin.TopNav.getExpiration(), '1day'); $.PrivateBin.TopNav.init();
assert.strictEqual($.PrivateBin.TopNav.getExpiration(), '1day');
cleanup(); cleanup();
} }
); );
@@ -557,7 +558,7 @@ describe('TopNav', function () {
'returns the selected files', 'returns the selected files',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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" ' +
@@ -566,16 +567,17 @@ 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();
results.push(
PrivateBin.TopNav.getFileList() === null
); );
addFileList(query('#file'), [ $.PrivateBin.TopNav.init();
results.push(
$.PrivateBin.TopNav.getFileList() === null
);
addFileList($('#file')[0], [
'../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'
@@ -599,23 +601,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 = [];
document.documentElement.innerHTML = $('body').html(
'<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();
results.push(
!PrivateBin.TopNav.getBurnAfterReading()
); );
query('#burnafterreading').checked = true; $.PrivateBin.TopNav.init();
results.push( results.push(
PrivateBin.TopNav.getBurnAfterReading() !$.PrivateBin.TopNav.getBurnAfterReading()
); );
query('#burnafterreading').checked = false; $('#burnafterreading').attr('checked', 'checked');
query('#burnafterreading').removeAttribute('checked');
results.push( results.push(
!PrivateBin.TopNav.getBurnAfterReading() $.PrivateBin.TopNav.getBurnAfterReading()
);
$('#burnafterreading').removeAttr('checked');
results.push(
!$.PrivateBin.TopNav.getBurnAfterReading()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -636,23 +638,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 = [];
document.documentElement.innerHTML = $('body').html(
'<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();
results.push(
!PrivateBin.TopNav.getOpenDiscussion()
); );
query('#opendiscussion').checked = true; $.PrivateBin.TopNav.init();
results.push( results.push(
PrivateBin.TopNav.getOpenDiscussion() !$.PrivateBin.TopNav.getOpenDiscussion()
); );
query('#opendiscussion').checked = false; $('#opendiscussion').attr('checked', 'checked');
query('#opendiscussion').removeAttribute('checked');
results.push( results.push(
!PrivateBin.TopNav.getOpenDiscussion() $.PrivateBin.TopNav.getOpenDiscussion()
);
$('#opendiscussion').removeAttr('checked');
results.push(
!$.PrivateBin.TopNav.getOpenDiscussion()
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -675,22 +677,23 @@ describe('TopNav', function () {
function (password) { function (password) {
password = password.replace(/\r+|\n+/g, ''); password = password.replace(/\r+|\n+/g, '');
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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();
results.push(
PrivateBin.TopNav.getPassword() === ''
); );
query('#passwordinput').value = password; $.PrivateBin.TopNav.init();
results.push( results.push(
PrivateBin.TopNav.getPassword() === password $.PrivateBin.TopNav.getPassword() === ''
); );
query('#passwordinput').value = ''; $('#passwordinput').val(password);
results.push( results.push(
PrivateBin.TopNav.getPassword() === '' $.PrivateBin.TopNav.getPassword() === password
);
$('#passwordinput').val('');
results.push(
$.PrivateBin.TopNav.getPassword() === ''
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -711,7 +714,7 @@ describe('TopNav', function () {
'returns the custom attachment element', 'returns the custom attachment element',
function () { function () {
let results = []; let results = [];
document.documentElement.innerHTML = $('body').html(
'<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" ' +
@@ -720,14 +723,15 @@ 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();
results.push(
!PrivateBin.TopNav.getCustomAttachment().classList.contains('test')
); );
query('#customattachment').classList.add('test'); $.PrivateBin.TopNav.init();
results.push( results.push(
PrivateBin.TopNav.getCustomAttachment().classList.contains('test') !$.PrivateBin.TopNav.getCustomAttachment().hasClass('test')
);
$('#customattachment').addClass('test');
results.push(
$.PrivateBin.TopNav.getCustomAttachment().hasClass('test')
); );
cleanup(); cleanup();
const result = results.every(element => element); const result = results.every(element => element);
@@ -749,7 +753,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
document.body.innerHTML = $('body').html(
'<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" ' +
@@ -766,14 +770,15 @@ 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.hideAllButtons(); $.PrivateBin.TopNav.init();
$.PrivateBin.TopNav.hideAllButtons();
assert.ok(query('#newbutton').classList.contains('hidden')); assert.ok($('#newbutton').hasClass('hidden'));
assert.ok(query('#clonebutton').classList.contains('hidden')); assert.ok($('#clonebutton').hasClass('hidden'));
assert.ok(query('#rawtextbutton').classList.contains('hidden')); assert.ok($('#rawtextbutton').hasClass('hidden'));
assert.ok(query('#qrcodelink').classList.contains('hidden')); assert.ok($('#qrcodelink').hasClass('hidden'));
cleanup(); cleanup();
} }
); );
@@ -788,17 +793,14 @@ describe('TopNav', function () {
it( it(
'displays raw text view correctly', 'displays raw text view correctly',
function () { function () {
const clean = globalThis.cleanup('', {url: 'https://privatebin.net/?0123456789abcdef#1'}); const clean = jsdom('', {url: 'https://privatebin.net/?0123456789abcdef#1'});
document.documentElement.innerHTML = ` $('body').html('<button id="rawtextbutton"></button>');
<li id="loadingindicator" class="hidden"></li>
<button id="rawtextbutton"></button>`;
const sample = 'example'; const sample = 'example';
PrivateBin.Alert.init(); // required because of locading indiator being used $.PrivateBin.PasteViewer.setText(sample);
PrivateBin.TopNav.init(); $.PrivateBin.Helper.reset();
PrivateBin.PasteViewer.setText(sample); $.PrivateBin.TopNav.init();
PrivateBin.Helper.reset(); $('#rawtextbutton').click();
query('#rawtextbutton').click(); assert.strictEqual($('pre').text(), sample);
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 = globalThis.cleanup('', {url: expected}); clean = jsdom('', {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 = globalThis.cleanup('', {url: expected}); clean = jsdom('', {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 = globalThis.cleanup('', {url: common.urlToString(url)}); const clean = jsdom('', {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 = globalThis.cleanup( var clean = jsdom(
'<' + element + ' id="' + id + '"></' + element + '>' '<' + element + ' id="' + id + '"></' + element + '>'
); );
var result = PrivateBin.UiHelper.isVisible(document.getElementById(id)); var result = $.PrivateBin.UiHelper.isVisible($('#' + 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() {
document.documentElement.innerHTML = $('body').html(
// 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,18 +15,14 @@ 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();
document.documentElement.innerHTML = $('body').html(
// TopNav expectsinitially hidden #emaillink BUTTON. // TopNav expectsinitially hidden #emaillink BUTTON.
'<nav><div id="navbar"><ul>' + '<nav><div id="navbar"><ul>' +
'<li>' + '<li>' +
@@ -42,41 +38,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 makeWindowOpenMock() { function stubWinOpen($element) {
const originalOpen = window.open; const win = $element[0].ownerDocument.defaultView;
let openedUrl = null;
let mockRestoreFn = null;
if (typeof jest !== 'undefined' && typeof jest.spyOn === 'function') { // Some helpers in privatebin.js expect a global document.
const spy = jest.spyOn(window, 'open').mockImplementation((url) => { global.document = win.document;
openedUrl = url;
return {}; let openedUrl = null;
}); const origOpen = win.open;
mockRestoreFn = () => spy.mockRestore();
} else { // Prefer simple assignment; if blocked, fall back to defineProperty.
window.open = function (url) { try {
win.open = function (url) {
openedUrl = url; openedUrl = url;
return {}; return {};
}; };
mockRestoreFn = () => { window.open = originalOpen; }; } catch (e) {
Object.defineProperty(win, 'open', {
value: function (url) {
openedUrl = url;
return {};
},
configurable: true,
writable: true
});
} }
return { return {
getUrl: () => openedUrl, getUrl: () => openedUrl,
restore: () => { restore: () => { try { win.open = origOpen; } catch (e) { /* suppress exception in restore */ } },
if (mockRestoreFn) { win
mockRestoreFn();
}
}
}; };
} }
@@ -88,23 +84,21 @@ function extractMailtoBody(mailtoUrl) {
} }
describe('Email - mail body content (short URL vs. fallback)', function () { describe('Email - mail body content (short URL vs. fallback)', function () {
beforeEach(function () { before(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 = document.getElementById('emaillink'); const $emailBtn = $('#emaillink');
assert.ok(!emailBtn.classList.contains('hidden'), '#emaillink should be visible after showEmailButton'); assert.ok(!$emailBtn.hasClass('hidden'), '#emaillink should be visible after showEmailButton');
const { getUrl, restore } = makeWindowOpenMock(); const { getUrl, restore } = stubWinOpen($emailBtn);
try { try {
emailBtn.click(); $emailBtn.trigger('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');
@@ -113,30 +107,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 = document.getElementById('emaillink'); const $emailBtn = $('#emaillink');
assert.ok(!emailBtn.classList.contains('hidden'), '#emaillink should be visible after showEmailButton'); assert.ok(!$emailBtn.hasClass('hidden'), '#emaillink should be visible after showEmailButton');
const { getUrl, restore } = makeWindowOpenMock(); const { getUrl, restore, win } = stubWinOpen($emailBtn);
try { try {
emailBtn.click(); $emailBtn.trigger('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(window.location.href), 'email body should include the fallback page URL'); assert.match(body, new RegExp(win.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.0.js' => 'sha512-lsHD5zxs4lu/NDzaaibe27Vd2t7Cy9JQ3qDHUvDfb4oZvKoWDNEhwUY+4bT3R68cGgpgCYp8U1x2ifeVxqurdQ==', 'js/purify-3.3.2.js' => 'sha512-I6igPVpf3xNghG92mujwqB6Zi3LpUTsni4bRuLnMThEGH6BDbsumv7373+AXHzA4OUlxGsym8ZxKFHy4xjYvkQ==',
'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.0.js', 'defer'); ?> <?php $this->_scriptTag('js/purify-3.3.2.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.0.js', 'defer'); ?> <?php $this->_scriptTag('js/purify-3.3.2.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 -->