diff --git a/js/common.js b/js/common.js index 44544478..093428c2 100644 --- a/js/common.js +++ b/js/common.js @@ -1,43 +1,12 @@ 'use strict'; -exports.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']; -exports.alnumString = exports.a2zString.concat(['0','1','2','3','4','5','6','7','8','9']); -exports.queryString = exports.alnumString.concat(['+','%','&','.','*','-','_']); -exports.base64String = exports.alnumString.concat(['+','/','=']).concat( - exports.a2zString.map(function(c) { - return c.toUpperCase(); - }) -); -// schemas supported by the whatwg-url library -exports.schemas = ['ftp','gopher','http','https','ws','wss']; -exports.supportedLanguages = ['de', 'es', 'fr', 'it', 'no', 'pl', 'pt', 'oc', 'ru', 'sl', 'zh']; -exports.mimeTypes = ['image/png', 'application/octet-stream']; - +// testing prerequisites global.jsc = require('jsverify'); global.jsdom = require('jsdom-global'); global.cleanup = global.jsdom(); global.fs = require('fs'); -/** - * character to HTML entity lookup table - * - * @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60} - */ -var entityMap = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '/': '/', - '`': '`', - '=': '=' - }, - logFile = fs.createWriteStream('test.log'), - mimeFile = fs.createReadStream('/etc/mime.types'), - mimeLine = ''; - +// application libraries to test global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); global.Base64 = require('./base64-2.1.9').Base64; @@ -51,6 +20,38 @@ global.DOMPurify = require('./purify.min'); require('./bootstrap-3.3.7'); require('./privatebin'); +// internal variables +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'], + alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), + queryString = alnumString.concat(['+','%','&','.','*','-','_']), + base64String = alnumString.concat(['+','/','=']).concat( + a2zString.map(function(c) { + return c.toUpperCase(); + }) + ), + schemas = ['ftp','gopher','http','https','ws','wss'], + supportedLanguages = ['de', 'es', 'fr', 'it', 'no', 'pl', 'pt', 'oc', 'ru', 'sl', 'zh'], + mimeTypes = ['image/png', 'application/octet-stream'], + /** + * character to HTML entity lookup table + * + * @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60} + */ + entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`', + '=': '=' + }, + logFile = fs.createWriteStream('test.log'), + mimeFile = fs.createReadStream('/etc/mime.types'), + mimeLine = ''; + // redirect console messages to log file console.info = console.warn = console.error = function () { logFile.write(Array.prototype.slice.call(arguments).join('') + '\n'); @@ -87,10 +88,12 @@ function parseMime(line) { line = line.substring(0, index); } if (line.length > 0) { - exports.mimeTypes.push(line); + mimeTypes.push(line); } } +// common testing helper functions + /** * convert all applicable characters to HTML entities * @@ -107,3 +110,28 @@ exports.htmlEntities = function(str) { }); } +// provides random lowercase characters from a to z +exports.jscA2zString = function() { + return jsc.elements(a2zString); +} + +// provides random lowercase alpha numeric characters (a to z and 0 to 9) +exports.jscAlnumString = function() { + return jsc.elements(alnumString); +} + +// provides random characters allowed in GET queries +exports.jscQueryString = function() { + return jsc.elements(queryString); +} + +// provides a random URL schema supported by the whatwg-url library +exports.jscSchemas = function() { + return jsc.elements(schemas); +} + +// provides a random supported language string +exports.jscSupportedLanguages = function() { + return jsc.elements(supportedLanguages); +} + diff --git a/js/test/Helper.js b/js/test/Helper.js index 81ea54c1..1701ec11 100644 --- a/js/test/Helper.js +++ b/js/test/Helper.js @@ -53,7 +53,7 @@ describe('Helper', function () { this.timeout(30000); jsc.property( 'selection contains content of given ID', - jsc.nearray(jsc.nearray(jsc.elements(common.alnumString))), + jsc.nearray(jsc.nearray(common.jscAlnumString())), 'nearray string', function (ids, contents) { var html = '', @@ -90,9 +90,9 @@ describe('Helper', function () { 'replaces URLs with anchors', 'string', jsc.elements(['http', 'https', 'ftp']), - jsc.nearray(jsc.elements(common.a2zString)), - jsc.array(jsc.elements(common.queryString)), - jsc.array(jsc.elements(common.queryString)), + jsc.nearray(common.jscA2zString()), + jsc.array(common.jscQueryString()), + jsc.array(common.jscQueryString()), 'string', function (prefix, schema, address, query, fragment, postfix) { var query = query.join(''), @@ -117,7 +117,7 @@ describe('Helper', function () { jsc.property( 'replaces magnet links with anchors', 'string', - jsc.array(jsc.elements(common.queryString)), + jsc.array(common.jscQueryString()), 'string', function (prefix, query, postfix) { var url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm,''), @@ -244,9 +244,9 @@ describe('Helper', function () { jsc.property( 'returns the URL without query & fragment', - jsc.elements(common.schemas), - jsc.nearray(jsc.elements(common.a2zString)), - jsc.array(jsc.elements(common.queryString)), + common.jscSchemas(), + jsc.nearray(common.jscA2zString()), + jsc.array(common.jscQueryString()), 'string', function (schema, address, query, fragment) { var expected = schema + '://' + address.join('') + '/', diff --git a/js/test/I18n.js b/js/test/I18n.js new file mode 100644 index 00000000..493a4a90 --- /dev/null +++ b/js/test/I18n.js @@ -0,0 +1,104 @@ +'use strict'; +var common = require('../common'); + +describe('I18n', function () { + describe('translate', function () { + before(function () { + $.PrivateBin.I18n.reset(); + }); + + jsc.property( + 'returns message ID unchanged if no translation found', + 'string', + function (messageId) { + messageId = messageId.replace(/%(s|d)/g, '%%'); + var plurals = [messageId, messageId + 's'], + fake = [messageId], + result = $.PrivateBin.I18n.translate(messageId); + $.PrivateBin.I18n.reset(); + + var alias = $.PrivateBin.I18n._(messageId); + $.PrivateBin.I18n.reset(); + + var p_result = $.PrivateBin.I18n.translate(plurals); + $.PrivateBin.I18n.reset(); + + var p_alias = $.PrivateBin.I18n._(plurals); + $.PrivateBin.I18n.reset(); + + var f_result = $.PrivateBin.I18n.translate(fake); + $.PrivateBin.I18n.reset(); + + var f_alias = $.PrivateBin.I18n._(fake); + $.PrivateBin.I18n.reset(); + + return messageId === result && messageId === alias && + messageId === p_result && messageId === p_alias && + messageId === f_result && messageId === f_alias; + } + ); + jsc.property( + 'replaces %s in strings with first given parameter', + 'string', + '(small nearray) string', + 'string', + function (prefix, params, postfix) { + prefix = prefix.replace(/%(s|d)/g, '%%'); + params[0] = params[0].replace(/%(s|d)/g, '%%'); + postfix = postfix.replace(/%(s|d)/g, '%%'); + var translation = prefix + params[0] + postfix; + params.unshift(prefix + '%s' + postfix); + var result = $.PrivateBin.I18n.translate.apply(this, params); + $.PrivateBin.I18n.reset(); + var alias = $.PrivateBin.I18n._.apply(this, params); + $.PrivateBin.I18n.reset(); + return translation === result && translation === alias; + } + ); + }); + + describe('getPluralForm', function () { + before(function () { + $.PrivateBin.I18n.reset(); + }); + + jsc.property( + 'returns valid key for plural form', + common.jscSupportedLanguages(), + 'integer', + function(language, n) { + $.PrivateBin.I18n.reset(language); + var result = $.PrivateBin.I18n.getPluralForm(n); + // arabic seems to have the highest plural count with 6 forms + return result >= 0 && result <= 5; + } + ); + }); + + // loading of JSON via AJAX needs to be tested in the browser, this just mocks it + // TODO: This needs to be tested using a browser. + describe('loadTranslations', function () { + this.timeout(30000); + before(function () { + $.PrivateBin.I18n.reset(); + }); + + jsc.property( + 'downloads and handles any supported language', + common.jscSupportedLanguages(), + function(language) { + var clean = jsdom('', {url: 'https://privatebin.net/', cookie: ['lang=' + language]}); + + $.PrivateBin.I18n.reset('en'); + $.PrivateBin.I18n.loadTranslations(); + $.PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json')); + var result = $.PrivateBin.I18n.translate('en'), + alias = $.PrivateBin.I18n._('en'); + + clean(); + return language === result && language === alias; + } + ); + }); +}); +