Jelajahi Sumber

Merge branch 'js-unit-testing'

El RIDO 8 tahun lalu
induk
melakukan
e40da8b1a6
10 mengubah file dengan 811 tambahan dan 49 penghapusan
  1. 0 2
      .gitattributes
  2. 4 2
      .styleci.yml
  3. 49 32
      js/privatebin.js
  4. 737 3
      js/test.js
  5. 2 2
      lib/Filter.php
  6. 13 2
      tpl/bootstrap.php
  7. 2 2
      tpl/page.php
  8. 1 1
      tst/ConfigurationTest.php
  9. 2 2
      tst/I18nTest.php
  10. 1 1
      tst/PrivateBinWithDbTest.php

+ 0 - 2
.gitattributes

@@ -2,8 +2,6 @@ doc/ export-ignore
 tst/ export-ignore
 tst/ export-ignore
 js/.istanbul.yml export-ignore
 js/.istanbul.yml export-ignore
 js/test.js export-ignore
 js/test.js export-ignore
-js/mocha-3.2.0.js export-ignore
-css/mocha-3.2.0.css export-ignore
 .codeclimate.yml export-ignore
 .codeclimate.yml export-ignore
 .csslintrc export-ignore
 .csslintrc export-ignore
 .dockerignore export-ignore
 .dockerignore export-ignore

+ 4 - 2
.styleci.yml

@@ -3,10 +3,11 @@ preset: recommended
 risky: false
 risky: false
 
 
 enabled:
 enabled:
-  - no_empty_comment
   - align_equals
   - align_equals
-  - long_array_syntax
   - concat_with_spaces
   - concat_with_spaces
+  - long_array_syntax
+  - no_empty_comment
+  - pre_increment
 
 
 disabled:
 disabled:
   - blank_line_after_opening_tag
   - blank_line_after_opening_tag
@@ -23,6 +24,7 @@ disabled:
   - phpdoc_separation
   - phpdoc_separation
   - phpdoc_single_line_var_spacing
   - phpdoc_single_line_var_spacing
   - phpdoc_summary
   - phpdoc_summary
+  - post_increment
   - short_array_syntax
   - short_array_syntax
   - single_line_after_imports
   - single_line_after_imports
   - unalign_equals
   - unalign_equals

+ 49 - 32
js/privatebin.js

@@ -124,7 +124,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                 range = document.body.createTextRange();
                 range = document.body.createTextRange();
                 range.moveToElementText(element);
                 range.moveToElementText(element);
                 range.select();
                 range.select();
-            } else if (window.getSelection){
+            } else if (window.getSelection) {
                 selection = window.getSelection();
                 selection = window.getSelection();
                 range = document.createRange();
                 range = document.createRange();
                 range.selectNodeContents(element);
                 range.selectNodeContents(element);
@@ -302,8 +302,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
      * internationalization module
      * internationalization module
      *
      *
      * @name I18n
      * @name I18n
-     * @param  {object} window
-     * @param  {object} document
      * @class
      * @class
      */
      */
     var I18n = (function () {
     var I18n = (function () {
@@ -414,7 +412,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                     var orgArguments = arguments;
                     var orgArguments = arguments;
                     $(document).on(languageLoadedEvent, function () {
                     $(document).on(languageLoadedEvent, function () {
                         // log to show that the previous error could be mitigated
                         // log to show that the previous error could be mitigated
-                        console.log('Fix missing translation of \'' + messageId + '\' with now loaded language ' + language);
+                        console.warn('Fix missing translation of \'' + messageId + '\' with now loaded language ' + language);
                         // re-execute this function
                         // re-execute this function
                         me.translate.apply(this, orgArguments);
                         me.translate.apply(this, orgArguments);
                     });
                     });
@@ -845,11 +843,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
      * everything directly UI-related, which fits nowhere else
      * everything directly UI-related, which fits nowhere else
      *
      *
      * @name   UiHelper
      * @name   UiHelper
-     * @param  {object} window
-     * @param  {object} document
      * @class
      * @class
      */
      */
-    var UiHelper = (function (window, document) {
+    var UiHelper = (function () {
         var me = {};
         var me = {};
 
 
         /**
         /**
@@ -866,7 +862,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         {
         {
             var currentLocation = Helper.baseUri();
             var currentLocation = Helper.baseUri();
             if (event.originalEvent.state === null && // no state object passed
             if (event.originalEvent.state === null && // no state object passed
-                event.originalEvent.target.location.href === currentLocation && // target location is home page
+                event.target.location.href === currentLocation && // target location is home page
                 window.location.href === currentLocation // and we are not already on the home page
                 window.location.href === currentLocation // and we are not already on the home page
             ) {
             ) {
                 // redirect to home page
                 // redirect to home page
@@ -958,6 +954,23 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             });
             });
         }
         }
 
 
+        /**
+         * trigger a history (pop) state change
+         *
+         * used to test the UiHelper.historyChange private function
+         *
+         * @name   UiHelper.mockHistoryChange
+         * @function
+         * @param  {string} state   (optional) state to mock
+         */
+        me.mockHistoryChange = function(state)
+        {
+            if (typeof state === 'undefined') {
+                state = null;
+            }
+            historyChange($.Event('popstate', {originalEvent: new PopStateEvent('popstate', {state: state}), target: window}));
+        }
+
         /**
         /**
          * initialize
          * initialize
          *
          *
@@ -973,7 +986,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         }
         }
 
 
         return me;
         return me;
-    })(window, document);
+    })();
 
 
     /**
     /**
      * Alert/error manager
      * Alert/error manager
@@ -989,12 +1002,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             $statusMessage,
             $statusMessage,
             $remainingTime;
             $remainingTime;
 
 
-        var currentIcon = [
-            'glyphicon-time', // loading icon
-            'glyphicon-info-sign', // status icon
-            '', // resevered for warning, not used yet
-            'glyphicon-alert' // error icon
-        ];
+        var currentIcon;
 
 
         var alertType = [
         var alertType = [
             'loading', // not in bootstrap, but using a good value here
             'loading', // not in bootstrap, but using a good value here
@@ -1090,7 +1098,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
          */
          */
         me.showStatus = function(message, icon, dismissable, autoclose)
         me.showStatus = function(message, icon, dismissable, autoclose)
         {
         {
-            console.log('status shown: ', message);
+            console.info('status shown: ', message);
             // @TODO: implement dismissable
             // @TODO: implement dismissable
             // @TODO: implement autoclose
             // @TODO: implement autoclose
 
 
@@ -1133,7 +1141,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
          */
          */
         me.showRemaining = function(message)
         me.showRemaining = function(message)
         {
         {
-            console.log('remaining message shown: ', message);
+            console.info('remaining message shown: ', message);
             handleNotification(1, $remainingTime, message);
             handleNotification(1, $remainingTime, message);
         }
         }
 
 
@@ -1151,7 +1159,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         me.showLoading = function(message, percentage, icon)
         me.showLoading = function(message, percentage, icon)
         {
         {
             if (typeof message !== 'undefined' && message !== null) {
             if (typeof message !== 'undefined' && message !== null) {
-                console.log('status changed: ', message);
+                console.info('status changed: ', message);
             }
             }
 
 
             // default message text
             // default message text
@@ -1238,6 +1246,13 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             $loadingIndicator = $('#loadingindicator');
             $loadingIndicator = $('#loadingindicator');
             $statusMessage = $('#status');
             $statusMessage = $('#status');
             $remainingTime = $('#remainingtime');
             $remainingTime = $('#remainingtime');
+
+            currentIcon = [
+                'glyphicon-time', // loading icon
+                'glyphicon-info-sign', // status icon
+                '', // reserved for warning, not used yet
+                'glyphicon-alert' // error icon
+            ];
         }
         }
 
 
         return me;
         return me;
@@ -1247,10 +1262,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
      * handles paste status/result
      * handles paste status/result
      *
      *
      * @name   PasteStatus
      * @name   PasteStatus
-     * @param  {object} window
      * @class
      * @class
      */
      */
-    var PasteStatus = (function (window) {
+    var PasteStatus = (function () {
         var me = {};
         var me = {};
 
 
         var $pasteSuccess,
         var $pasteSuccess,
@@ -1351,7 +1365,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                     ];
                     ];
 
 
                 Alert.showRemaining([expirationLabel, expiration[0]]);
                 Alert.showRemaining([expirationLabel, expiration[0]]);
-                $remainingTime.removeClass('foryoureyesonly')
+                $remainingTime.removeClass('foryoureyesonly');
             } else {
             } else {
                 // never expires
                 // never expires
                 return;
                 return;
@@ -1383,7 +1397,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
          */
          */
         me.init = function()
         me.init = function()
         {
         {
-            $pasteSuccess = $('#pasteSuccess');
+            $pasteSuccess = $('#pastesuccess');
             // $pasteUrl is saved in me.createPasteNotification() after creation
             // $pasteUrl is saved in me.createPasteNotification() after creation
             $remainingTime = $('#remainingtime');
             $remainingTime = $('#remainingtime');
             $shortenButton = $('#shortenbutton');
             $shortenButton = $('#shortenbutton');
@@ -1393,7 +1407,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         }
         }
 
 
         return me;
         return me;
-    })(window);
+    })();
 
 
     /**
     /**
      * password prompt
      * password prompt
@@ -1454,7 +1468,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                 throw 'password prompt canceled';
                 throw 'password prompt canceled';
             }
             }
             if (password.length === 0) {
             if (password.length === 0) {
-                // recursive…
+                // recurse…
                 return me.requestPassword();
                 return me.requestPassword();
             }
             }
 
 
@@ -1462,7 +1476,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         }
         }
 
 
         /**
         /**
-         * getthe cached password
+         * get the cached password
          *
          *
          * If you do not get a password with this function
          * If you do not get a password with this function
          * (returns an empty string), use requestPassword.
          * (returns an empty string), use requestPassword.
@@ -1825,7 +1839,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
          *
          *
          * @name   PasteViewer.setFormat
          * @name   PasteViewer.setFormat
          * @function
          * @function
-         * @param {string} newFormat the the new format
+         * @param {string} newFormat the new format
          */
          */
         me.setFormat = function(newFormat)
         me.setFormat = function(newFormat)
         {
         {
@@ -1834,7 +1848,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
                 return;
                 return;
             }
             }
 
 
-            // needs to update display too, if from or to Markdown is switched
+            // needs to update display too, if we switch from or to Markdown
             if (format === 'markdown' || newFormat === 'markdown') {
             if (format === 'markdown' || newFormat === 'markdown') {
                 isDisplayed = false;
                 isDisplayed = false;
             }
             }
@@ -1963,6 +1977,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
 
 
             // get default option from template/HTML or fall back to set value
             // get default option from template/HTML or fall back to set value
             format = Model.getFormatDefault() || format;
             format = Model.getFormatDefault() || format;
+            text = '';
+            isDisplayed = false;
+            isChanged = true;
         }
         }
 
 
         return me;
         return me;
@@ -2639,7 +2656,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         me.showViewButtons = function()
         me.showViewButtons = function()
         {
         {
             if (viewButtonsDisplayed) {
             if (viewButtonsDisplayed) {
-                console.log('showViewButtons: view buttons are already displayed');
+                console.warn('showViewButtons: view buttons are already displayed');
                 return;
                 return;
             }
             }
 
 
@@ -2659,7 +2676,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         me.hideViewButtons = function()
         me.hideViewButtons = function()
         {
         {
             if (!viewButtonsDisplayed) {
             if (!viewButtonsDisplayed) {
-                console.log('hideViewButtons: view buttons are already hidden');
+                console.warn('hideViewButtons: view buttons are already hidden');
                 return;
                 return;
             }
             }
 
 
@@ -2691,7 +2708,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         me.showCreateButtons = function()
         me.showCreateButtons = function()
         {
         {
             if (createButtonsDisplayed) {
             if (createButtonsDisplayed) {
-                console.log('showCreateButtons: create buttons are already displayed');
+                console.warn('showCreateButtons: create buttons are already displayed');
                 return;
                 return;
             }
             }
 
 
@@ -2716,7 +2733,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
         me.hideCreateButtons = function()
         me.hideCreateButtons = function()
         {
         {
             if (!createButtonsDisplayed) {
             if (!createButtonsDisplayed) {
-                console.log('hideCreateButtons: create buttons are already hidden');
+                console.warn('hideCreateButtons: create buttons are already hidden');
                 return;
                 return;
             }
             }
 
 
@@ -3968,7 +3985,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
             Uploader.setUnencryptedData('deletetoken', deleteToken);
             Uploader.setUnencryptedData('deletetoken', deleteToken);
 
 
             Uploader.setFailure(function () {
             Uploader.setFailure(function () {
-                Controller.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
+                Alert.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
             })
             })
             Uploader.run();
             Uploader.run();
         }
         }

+ 737 - 3
js/test.js

@@ -22,11 +22,16 @@ global.sjcl = require('./sjcl-1.0.6');
 global.Base64 = require('./base64-2.1.9').Base64;
 global.Base64 = require('./base64-2.1.9').Base64;
 global.RawDeflate = require('./rawdeflate-0.5').RawDeflate;
 global.RawDeflate = require('./rawdeflate-0.5').RawDeflate;
 global.RawDeflate.inflate = require('./rawinflate-0.3').RawDeflate.inflate;
 global.RawDeflate.inflate = require('./rawinflate-0.3').RawDeflate.inflate;
+require('./prettify');
+global.prettyPrint = window.PR.prettyPrint;
+global.prettyPrintOne = window.PR.prettyPrintOne;
+global.showdown = require('./showdown-1.6.1');
+require('./bootstrap-3.3.7');
 require('./privatebin');
 require('./privatebin');
 
 
 // redirect console messages to log file
 // redirect console messages to log file
-console.warn = console.error = function (msg) {
-    logFile.write(msg + '\n');
+console.info = console.warn = console.error = function () {
+    logFile.write(Array.prototype.slice.call(arguments).join('') + '\n');
 }
 }
 
 
 describe('Helper', function () {
 describe('Helper', function () {
@@ -182,7 +187,7 @@ describe('Helper', function () {
             jsc.array(jsc.elements(queryString)),
             jsc.array(jsc.elements(queryString)),
             'string',
             'string',
             function (prefix, query, postfix) {
             function (prefix, query, postfix) {
-                var url = 'magnet:?' + query.join(''),
+                var url = 'magnet:?' + query.join('').replace(/^&+|&+$/gm,''),
                     prefix = $.PrivateBin.Helper.htmlEntities(prefix),
                     prefix = $.PrivateBin.Helper.htmlEntities(prefix),
                     postfix = $.PrivateBin.Helper.htmlEntities(postfix),
                     postfix = $.PrivateBin.Helper.htmlEntities(postfix),
                     element = $('<div>' + prefix + url + ' ' + postfix + '</div>');
                     element = $('<div>' + prefix + url + ' ' + postfix + '</div>');
@@ -599,6 +604,82 @@ describe('Model', function () {
         );
         );
     });
     });
 
 
+    describe('getFormatDefault', function () {
+        before(function () {
+            $.PrivateBin.Model.reset();
+            cleanup();
+        });
+
+        jsc.property(
+            'returns the contents of the element with id "pasteFormatter"',
+            'array asciinestring',
+            'string',
+            'small nat',
+            function (keys, value, key) {
+                keys = keys.map($.PrivateBin.Helper.htmlEntities);
+                value = $.PrivateBin.Helper.htmlEntities(value);
+                var content = keys.length > key ? keys[key] : (keys.length > 0 ? keys[0] : 'null'),
+                    contents = '<select id="pasteFormatter" name="pasteFormatter">';
+                keys.forEach(function(item) {
+                    contents += '<option value="' + item + '"';
+                    if (item === content) {
+                        contents += ' selected="selected"';
+                    }
+                    contents += '>' + value + '</option>';
+                });
+                contents += '</select>';
+                $('body').html(contents);
+                var result = $.PrivateBin.Helper.htmlEntities(
+                    $.PrivateBin.Model.getFormatDefault()
+                );
+                $.PrivateBin.Model.reset();
+                return content === result;
+            }
+        );
+    });
+
+    describe('hasCipherData', function () {
+        before(function () {
+            $.PrivateBin.Model.reset();
+            cleanup();
+        });
+
+        jsc.property(
+            'checks if the element with id "cipherdata" contains any data',
+            'asciistring',
+            function (value) {
+                value = $.PrivateBin.Helper.htmlEntities(value).trim();
+                $('body').html('<div id="cipherdata">' + value + '</div>');
+                $.PrivateBin.Model.init();
+                var result = $.PrivateBin.Model.hasCipherData();
+                $.PrivateBin.Model.reset();
+                return (value.length > 0) === result;
+            }
+        );
+    });
+
+    describe('getCipherData', function () {
+        before(function () {
+            $.PrivateBin.Model.reset();
+            cleanup();
+        });
+
+        jsc.property(
+            'returns the contents of the element with id "cipherdata"',
+            'asciistring',
+            function (value) {
+                value = $.PrivateBin.Helper.htmlEntities(value).trim();
+                $('body').html('<div id="cipherdata">' + value + '</div>');
+                $.PrivateBin.Model.init();
+                var result = $.PrivateBin.Helper.htmlEntities(
+                    $.PrivateBin.Model.getCipherData()
+                );
+                $.PrivateBin.Model.reset();
+                return value === result;
+            }
+        );
+    });
+
     describe('getPasteId', function () {
     describe('getPasteId', function () {
         this.timeout(30000);
         this.timeout(30000);
         before(function () {
         before(function () {
@@ -710,4 +791,657 @@ describe('Model', function () {
             }
             }
         );
         );
     });
     });
+
+    describe('getTemplate', function () {
+        before(function () {
+            $.PrivateBin.Model.reset();
+            cleanup();
+        });
+
+        jsc.property(
+            'returns the contents of the element with id "[name]template"',
+            jsc.nearray(jsc.elements(alnumString)),
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.nearray(jsc.elements(alnumString)),
+            function (id, element, value) {
+                id = id.join('');
+                element = element.join('');
+                value = value.join('').trim();
+
+                // <br>, <hr>, <img> and <wbr> tags can't contain strings,
+                // table tags can't be alone, so test with a <p> instead
+                if (['br', 'col', 'hr', 'img', 'tr', 'td', 'th', 'wbr'].indexOf(element) >= 0) {
+                    element = 'p';
+                }
+
+                $('body').html(
+                    '<div id="templates"><' + element + ' id="' + id +
+                    'template">' + value + '</' + element + '></div>'
+                );
+                $.PrivateBin.Model.init();
+                var template = '<' + element + ' id="' + id + '">' + value +
+                    '</' + element + '>',
+                    result = $.PrivateBin.Model.getTemplate(id).wrap('<p/>').parent().html();
+                $.PrivateBin.Model.reset();
+                return template === result;
+            }
+        );
+    });
+});
+
+describe('UiHelper', function () {
+    // TODO: As per https://github.com/tmpvar/jsdom/issues/1565 there is no navigation support in jsdom, yet.
+    // for now we use a mock function to trigger the event
+    describe('historyChange', function () {
+        this.timeout(30000);
+        before(function () {
+            $.PrivateBin.Helper.reset();
+        });
+
+        jsc.property(
+            'redirects to home, when the state is null',
+            jsc.elements(schemas),
+            jsc.nearray(jsc.elements(a2zString)),
+            function (schema, address) {
+                var expected = schema + '://' + address.join('') + '/',
+                    clean = jsdom('', {url: expected});
+
+                // make window.location.href writable
+                Object.defineProperty(window.location, 'href', {
+                    writable: true,
+                    value: window.location.href
+                });
+                $.PrivateBin.UiHelper.mockHistoryChange();
+                $.PrivateBin.Helper.reset();
+                var result = window.location.href;
+                clean();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'does not redirect to home, when a new paste is created',
+            jsc.elements(schemas),
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.array(jsc.elements(queryString)),
+            jsc.nearray(jsc.elements(base64String)),
+            function (schema, address, query, fragment) {
+                var expected = schema + '://' + address.join('') + '/' + '?' +
+                               query.join('') + '#' + fragment.join(''),
+                    clean = jsdom('', {url: expected});
+
+                // make window.location.href writable
+                Object.defineProperty(window.location, 'href', {
+                    writable: true,
+                    value: window.location.href
+                });
+                $.PrivateBin.UiHelper.mockHistoryChange([
+                    {type: 'newpaste'}, '', expected
+                ]);
+                $.PrivateBin.Helper.reset();
+                var result = window.location.href;
+                clean();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('reloadHome', function () {
+        this.timeout(30000);
+        before(function () {
+            $.PrivateBin.Helper.reset();
+        });
+
+        jsc.property(
+            'redirects to home',
+            jsc.elements(schemas),
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.array(jsc.elements(queryString)),
+            jsc.nearray(jsc.elements(base64String)),
+            function (schema, address, query, fragment) {
+                var expected = schema + '://' + address.join('') + '/',
+                    clean = jsdom('', {
+                        url: expected + '?' + query.join('') + '#' + fragment.join('')
+                    });
+
+                // make window.location.href writable
+                Object.defineProperty(window.location, 'href', {
+                    writable: true,
+                    value: window.location.href
+                });
+                $.PrivateBin.UiHelper.reloadHome();
+                $.PrivateBin.Helper.reset();
+                var result = window.location.href;
+                clean();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('isVisible', function () {
+        // TODO As per https://github.com/tmpvar/jsdom/issues/1048 there is no layout support in jsdom, yet.
+        // once it is supported or a workaround is found, uncomment the section below
+        /*
+        before(function () {
+            $.PrivateBin.Helper.reset();
+        });
+
+        jsc.property(
+            'detect visible elements',
+            jsc.nearray(jsc.elements(alnumString)),
+            jsc.nearray(jsc.elements(a2zString)),
+            function (id, element) {
+                id = id.join('');
+                element = element.join('');
+                var clean = jsdom(
+                    '<' + element + ' id="' + id + '"></' + element + '>'
+                );
+                var result = $.PrivateBin.UiHelper.isVisible($('#' + id));
+                clean();
+                return result;
+            }
+        );
+        */
+    });
+
+    describe('scrollTo', function () {
+        // TODO Did not find a way to test that, see isVisible test above
+    });
+});
+
+describe('Alert', function () {
+    describe('showStatus', function () {
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'shows a status message',
+            jsc.array(jsc.elements(alnumString)),
+            jsc.array(jsc.elements(alnumString)),
+            function (icon, message) {
+                icon = icon.join('');
+                message = message.join('');
+                var expected = '<div id="status" role="alert" ' +
+                    'class="statusmessage alert alert-info"><span ' +
+                    'class="glyphicon glyphicon-' + icon +
+                    '" aria-hidden="true"></span> ' + message + '</div>';
+                $('body').html(
+                    '<div id="status" role="alert" class="statusmessage ' +
+                    'alert alert-info hidden"><span class="glyphicon ' +
+                    'glyphicon-info-sign" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showStatus(message, icon);
+                var result = $('body').html();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('showError', function () {
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'shows an error message',
+            jsc.array(jsc.elements(alnumString)),
+            jsc.array(jsc.elements(alnumString)),
+            function (icon, message) {
+                icon = icon.join('');
+                message = message.join('');
+                var expected = '<div id="errormessage" role="alert" ' +
+                    'class="statusmessage alert alert-danger"><span ' +
+                    'class="glyphicon glyphicon-' + icon +
+                    '" aria-hidden="true"></span> ' + message + '</div>';
+                $('body').html(
+                    '<div id="errormessage" role="alert" class="statusmessage ' +
+                    'alert alert-danger hidden"><span class="glyphicon ' +
+                    'glyphicon-alert" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showError(message, icon);
+                var result = $('body').html();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('showRemaining', function () {
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'shows remaining time',
+            jsc.array(jsc.elements(alnumString)),
+            jsc.array(jsc.elements(alnumString)),
+            'integer',
+            function (message, string, number) {
+                message = message.join('');
+                string = string.join('');
+                var expected = '<div id="remainingtime" role="alert" ' +
+                    'class="alert alert-info"><span ' +
+                    'class="glyphicon glyphicon-fire" aria-hidden="true">' +
+                    '</span> ' + string + message + number + '</div>';
+                $('body').html(
+                    '<div id="remainingtime" role="alert" class="hidden ' +
+                    'alert alert-info"><span class="glyphicon ' +
+                    'glyphicon-fire" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
+                var result = $('body').html();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('showLoading', function () {
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'shows a loading message',
+            jsc.array(jsc.elements(alnumString)),
+            jsc.array(jsc.elements(alnumString)),
+            'integer',
+            function (icon, message, number) {
+                icon = icon.join('');
+                message = message.join('');
+                var default_message = 'Loading…';
+                if (message.length == 0) {
+                    message = default_message;
+                }
+                var expected = '<ul class="nav navbar-nav"><li ' +
+                    'id="loadingindicator" class="navbar-text"><span ' +
+                    'class="glyphicon glyphicon-' + icon +
+                    '" aria-hidden="true"></span> ' + message + '</li></ul>';
+                $('body').html(
+                    '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
+                    'class="navbar-text hidden"><span class="glyphicon ' +
+                    'glyphicon-time" aria-hidden="true"></span> ' +
+                    default_message + '</li></ul>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showLoading(message, number, icon);
+                var result = $('body').html();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('hideLoading', function () {
+        before(function () {
+            cleanup();
+        });
+
+        it(
+            'hides the loading message',
+            function() {
+                $('body').html(
+                    '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
+                    'class="navbar-text"><span class="glyphicon ' +
+                    'glyphicon-time" aria-hidden="true"></span> ' +
+                    'Loading…</li></ul>'
+                );
+                $('body').addClass('loading');
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.hideLoading();
+                return !$('body').hasClass('loading') &&
+                    $('#loadingindicator').hasClass('hidden');
+            }
+        );
+    });
+
+    describe('hideMessages', function () {
+        before(function () {
+            cleanup();
+        });
+
+        it(
+            'hides all messages',
+            function() {
+                $('body').html(
+                    '<div id="status" role="alert" class="statusmessage ' +
+                    'alert alert-info"><span class="glyphicon ' +
+                    'glyphicon-info-sign" aria-hidden="true"></span> </div>' +
+                    '<div id="errormessage" role="alert" class="statusmessage ' +
+                    'alert alert-danger"><span class="glyphicon ' +
+                    'glyphicon-alert" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.hideMessages();
+                return $('#statusmessage').hasClass('hidden') &&
+                    $('#errormessage').hasClass('hidden');
+            }
+        );
+    });
+
+    describe('setCustomHandler', function () {
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'calls a given handler function',
+            'nat 3',
+            jsc.array(jsc.elements(alnumString)),
+            function (trigger, message) {
+                message = message.join('');
+                var handlerCalled = false,
+                    default_message = 'Loading…',
+                    functions = [
+                        $.PrivateBin.Alert.showStatus,
+                        $.PrivateBin.Alert.showError,
+                        $.PrivateBin.Alert.showRemaining,
+                        $.PrivateBin.Alert.showLoading
+                    ];
+                if (message.length == 0) {
+                    message = default_message;
+                }
+                $('body').html(
+                    '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
+                    'class="navbar-text hidden"><span class="glyphicon ' +
+                    'glyphicon-time" aria-hidden="true"></span> ' +
+                    default_message + '</li></ul>' +
+                    '<div id="remainingtime" role="alert" class="hidden ' +
+                    'alert alert-info"><span class="glyphicon ' +
+                    'glyphicon-fire" aria-hidden="true"></span> </div>' +
+                    '<div id="status" role="alert" class="statusmessage ' +
+                    'alert alert-info"><span class="glyphicon ' +
+                    'glyphicon-info-sign" aria-hidden="true"></span> </div>' +
+                    '<div id="errormessage" role="alert" class="statusmessage ' +
+                    'alert alert-danger"><span class="glyphicon ' +
+                    'glyphicon-alert" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.setCustomHandler(function(id, $element) {
+                    handlerCalled = true;
+                    return jsc.random(0, 1) ? true : $element;
+                });
+                functions[trigger](message);
+                return handlerCalled;
+            }
+        );
+    });
+});
+
+describe('PasteStatus', function () {
+    describe('createPasteNotification', function () {
+        this.timeout(30000);
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'creates a notification after a successfull paste upload',
+            jsc.elements(schemas),
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.array(jsc.elements(queryString)),
+            'string',
+            jsc.elements(schemas),
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.array(jsc.elements(queryString)),
+            function (
+                schema1, address1, query1, fragment1,
+                schema2, address2, query2
+            ) {
+                var expected1 = schema1 + '://' + address1.join('') + '/?' +
+                    encodeURI(query1.join('').replace(/^&+|&+$/gm,'') + '#' + fragment1),
+                    expected2 = schema2 + '://' + address2.join('') + '/?' +
+                    encodeURI(query2.join('')),
+                    clean = jsdom();
+                $('body').html('<div><div id="deletelink"></div><div id="pastelink"></div></div>');
+                $.PrivateBin.PasteStatus.init();
+                $.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
+                var result1 = $('#pasteurl')[0].href,
+                    result2 = $('#deletelink a')[0].href;
+                clean();
+                return result1 == expected1 && result2 == expected2;
+            }
+        );
+    });
+
+    describe('showRemainingTime', function () {
+        this.timeout(30000);
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'shows burn after reading message or remaining time',
+            'bool',
+            'nat',
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.nearray(jsc.elements(a2zString)),
+            jsc.array(jsc.elements(queryString)),
+            'string',
+            function (
+                burnafterreading, remaining_time,
+                schema, address, query, fragment
+            ) {
+                var clean = jsdom('', {
+                        url: schema.join('') + '://' + address.join('') +
+                             '/?' + queryString + '#' + fragment
+                    });
+                $('body').html('<div id="remainingtime" class="hidden"></div>');
+                $.PrivateBin.PasteStatus.init();
+                $.PrivateBin.PasteStatus.showRemainingTime({
+                    'burnafterreading': burnafterreading,
+                    'remaining_time': remaining_time,
+                    'expire_date': remaining_time ? ((new Date()).getTime() / 1000) + remaining_time : 0
+                });
+                if (burnafterreading) {
+                    var result = $('#remainingtime').hasClass('foryoureyesonly') &&
+                                !$('#remainingtime').hasClass('hidden');
+                } else if (remaining_time) {
+                    var result =!$('#remainingtime').hasClass('foryoureyesonly') &&
+                                !$('#remainingtime').hasClass('hidden');
+                } else {
+                    var result = $('#remainingtime').hasClass('hidden') &&
+                                !$('#remainingtime').hasClass('foryoureyesonly');
+                }
+                clean();
+                return result;
+            }
+        );
+    });
+
+    describe('hideMessages', function () {
+        before(function () {
+            cleanup();
+        });
+
+        it(
+            'hides all messages',
+            function() {
+                $('body').html(
+                    '<div id="remainingtime"></div><div id="pastesuccess"></div>'
+                );
+                $.PrivateBin.PasteStatus.init();
+                $.PrivateBin.PasteStatus.hideMessages();
+                return $('#remainingtime').hasClass('hidden') &&
+                    $('#pastesuccess').hasClass('hidden');
+            }
+        );
+    });
 });
 });
+
+describe('Prompt', function () {
+    // TODO: this does not test the prompt() fallback, since that isn't available
+    //       in nodejs -> replace the prompt in the "page" template with a modal
+    describe('requestPassword & getPassword', function () {
+        this.timeout(30000);
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'returns the password fed into the dialog',
+            'string',
+            function (password) {
+                password = password.replace(/\r+/g, '');
+                var clean = jsdom('', {url: 'ftp://example.com/#0'});
+                $('body').html(
+                    '<div id="passwordmodal" class="modal fade" role="dialog">' +
+                    '<div class="modal-dialog"><div class="modal-content">' +
+                    '<div class="modal-body"><form id="passwordform" role="form">' +
+                    '<div class="form-group"><input id="passworddecrypt" ' +
+                    'type="password" class="form-control" placeholder="Enter ' +
+                    'password"></div><button type="submit">Decrypt</button>' +
+                    '</form></div></div></div></div><div id="cipherdata">{}</div>'
+                );
+                $.PrivateBin.Model.init();
+                $.PrivateBin.Prompt.init();
+                $.PrivateBin.Prompt.requestPassword();
+                $('#passworddecrypt').val(password);
+                $('#passwordform').submit();
+                var result = $.PrivateBin.Prompt.getPassword();
+                clean();
+                return result == password;
+            }
+        );
+    });
+});
+
+describe('Editor', function () {
+    describe('show, hide, getText, setText & isPreview', function () {
+        this.timeout(30000);
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'returns text fed into the textarea, handles editor tabs',
+            'string',
+            function (text) {
+                var clean = jsdom(),
+                    results = [];
+                $('body').html(
+                    '<ul id="editorTabs" class="nav nav-tabs hidden"><li ' +
+                    'role="presentation" class="active"><a id="messageedit" ' +
+                    'href="#">Editor</a></li><li role="presentation"><a ' +
+                    'id="messagepreview" href="#">Preview</a></li></ul><div ' +
+                    'id="placeholder" class="hidden">+++ no paste text +++</div>' +
+                    '<div id="prettymessage" class="hidden"><pre id="prettyprint" ' +
+                    'class="prettyprint linenums:1"></pre></div><div ' +
+                    'id="plaintext" class="hidden"></div><p><textarea ' +
+                    'id="message" name="message" cols="80" rows="25" ' +
+                    'class="form-control hidden"></textarea></p>'
+                );
+                $.PrivateBin.Editor.init();
+                results.push(
+                    $('#editorTabs').hasClass('hidden') &&
+                    $('#message').hasClass('hidden')
+                );
+                $.PrivateBin.Editor.show();
+                results.push(
+                    !$('#editorTabs').hasClass('hidden') &&
+                    !$('#message').hasClass('hidden')
+                );
+                $.PrivateBin.Editor.hide();
+                results.push(
+                    $('#editorTabs').hasClass('hidden') &&
+                    $('#message').hasClass('hidden')
+                );
+                $.PrivateBin.Editor.show();
+                $.PrivateBin.Editor.focusInput();
+                results.push(
+                    $.PrivateBin.Editor.getText().length == 0
+                );
+                $.PrivateBin.Editor.setText(text);
+                results.push(
+                    $.PrivateBin.Editor.getText() == $('#message').val()
+                );
+                $.PrivateBin.Editor.setText();
+                results.push(
+                    !$.PrivateBin.Editor.isPreview() &&
+                    !$('#message').hasClass('hidden')
+                );
+                $('#messagepreview').click();
+                results.push(
+                    $.PrivateBin.Editor.isPreview() &&
+                    $('#message').hasClass('hidden')
+                );
+                $('#messageedit').click();
+                results.push(
+                    !$.PrivateBin.Editor.isPreview() &&
+                    !$('#message').hasClass('hidden')
+                );
+                clean();
+                return results.every(element => element);
+            }
+        );
+    });
+});
+
+describe('PasteViewer', function () {
+    describe('run, hide, getText, setText, getFormat, setFormat & isPrettyPrinted', function () {
+        this.timeout(30000);
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'displays text according to format',
+            jsc.elements(['plaintext', 'markdown', 'syntaxhighlighting']),
+            'nestring',
+            function (format, text) {
+                var clean = jsdom(),
+                    results = [];
+                $('body').html(
+                    '<div id="placeholder" class="hidden">+++ no paste 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(format);
+                $.PrivateBin.PasteViewer.setText('');
+                results.push(
+                    $('#placeholder').hasClass('hidden') &&
+                    $('#prettymessage').hasClass('hidden') &&
+                    $('#plaintext').hasClass('hidden') &&
+                    $.PrivateBin.PasteViewer.getFormat() == format &&
+                    $.PrivateBin.PasteViewer.getText() == ''
+                );
+                $.PrivateBin.PasteViewer.run();
+                results.push(
+                    !$('#placeholder').hasClass('hidden') &&
+                    $('#prettymessage').hasClass('hidden') &&
+                    $('#plaintext').hasClass('hidden')
+                );
+                $.PrivateBin.PasteViewer.hide();
+                results.push(
+                    $('#placeholder').hasClass('hidden') &&
+                    $('#prettymessage').hasClass('hidden') &&
+                    $('#plaintext').hasClass('hidden')
+                );
+                $.PrivateBin.PasteViewer.setText(text);
+                $.PrivateBin.PasteViewer.run();
+                results.push(
+                    $('#placeholder').hasClass('hidden') &&
+                    !$.PrivateBin.PasteViewer.isPrettyPrinted() &&
+                    $.PrivateBin.PasteViewer.getText() == text
+                );
+                if (format == 'markdown') {
+                    results.push(
+                        $('#prettymessage').hasClass('hidden') &&
+                        !$('#plaintext').hasClass('hidden')
+                    );
+                } else {
+                    results.push(
+                        !$('#prettymessage').hasClass('hidden') &&
+                        $('#plaintext').hasClass('hidden')
+                    );
+                }
+                clean();
+                return results.every(element => element);
+            }
+        );
+    });
+});
+

+ 2 - 2
lib/Filter.php

@@ -64,7 +64,7 @@ class Filter
         $i   = 0;
         $i   = 0;
         while (($size / 1024) >= 1) {
         while (($size / 1024) >= 1) {
             $size = $size / 1024;
             $size = $size / 1024;
-            $i++;
+            ++$i;
         }
         }
         return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . I18n::_($iec[$i]);
         return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . I18n::_($iec[$i]);
     }
     }
@@ -82,7 +82,7 @@ class Filter
     public static function slowEquals($a, $b)
     public static function slowEquals($a, $b)
     {
     {
         $diff = strlen($a) ^ strlen($b);
         $diff = strlen($a) ^ strlen($b);
-        for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
+        for ($i = 0; $i < strlen($a) && $i < strlen($b); ++$i) {
             $diff |= ord($a[$i]) ^ ord($b[$i]);
             $diff |= ord($a[$i]) ^ ord($b[$i]);
         }
         }
         return $diff === 0;
         return $diff === 0;

+ 13 - 2
tpl/bootstrap.php

@@ -69,7 +69,7 @@ if ($MARKDOWN):
 <?php
 <?php
 endif;
 endif;
 ?>
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-gTAGAlxWTLH1PaXA4q9l0kME8wPPWi2iub5uU7HZ3A2bisLtjMIsaZ/Ecu4+sawFvvedAzbh9Y4rR34Ct3oF5Q==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-NWkO7YojpZyTn/xpIjVwo+VBSVKIfxHjE9NC41GzcWgt3w36XNZ+wYZZjXLJh44rBj9qBmZiXvvt9g5iNkadlg==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
 		<![endif]-->
@@ -230,6 +230,17 @@ if ($isCpct):
 ?>
 ?>
 						</ul>
 						</ul>
 						<select id="pasteFormatter" name="pasteFormatter" class="hidden">
 						<select id="pasteFormatter" name="pasteFormatter" class="hidden">
+<?php
+    foreach ($FORMATTER as $key => $value):
+?>
+							<option value="<?php echo $key; ?>"<?php
+        if ($key == $FORMATTERDEFAULT):
+?> selected="selected"<?php
+        endif;
+?>><?php echo $value; ?></option>
+<?php
+    endforeach;
+?>
 						</select>
 						</select>
 					</li>
 					</li>
 <?php
 <?php
@@ -423,7 +434,7 @@ endif;
 					<a href="https://www.opera.com/">Opera</a>,
 					<a href="https://www.opera.com/">Opera</a>,
 					<a href="https://www.google.com/chrome">Chrome</a>…
 					<a href="https://www.google.com/chrome">Chrome</a>…
 				</div>
 				</div>
-				<div id="pasteSuccess" role="alert" class="hidden alert alert-success">
+				<div id="pastesuccess" role="alert" class="hidden alert alert-success">
 					<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
 					<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
 					<div id="deletelink"></div>
 					<div id="deletelink"></div>
 					<div id="pastelink">
 					<div id="pastelink">

+ 2 - 2
tpl/page.php

@@ -47,7 +47,7 @@ if ($MARKDOWN):
 <?php
 <?php
 endif;
 endif;
 ?>
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-gTAGAlxWTLH1PaXA4q9l0kME8wPPWi2iub5uU7HZ3A2bisLtjMIsaZ/Ecu4+sawFvvedAzbh9Y4rR34Ct3oF5Q==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-NWkO7YojpZyTn/xpIjVwo+VBSVKIfxHjE9NC41GzcWgt3w36XNZ+wYZZjXLJh44rBj9qBmZiXvvt9g5iNkadlg==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
 		<![endif]-->
@@ -184,7 +184,7 @@ if (strlen($LANGUAGESELECTION)):
 endif;
 endif;
 ?>
 ?>
 				</div>
 				</div>
-				<div id="pasteresult" class="hidden">
+				<div id="pastesuccess" class="hidden">
 					<div id="deletelink"></div>
 					<div id="deletelink"></div>
 					<div id="pastelink">
 					<div id="pastelink">
 <?php
 <?php

+ 1 - 1
tst/ConfigurationTest.php

@@ -142,7 +142,7 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
 
 
     public function testHandleConfigFileRename()
     public function testHandleConfigFileRename()
     {
     {
-        $options                           = $this->_options;
+        $options  = $this->_options;
         Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample', $options);
         Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample', $options);
 
 
         $options['main']['opendiscussion'] = true;
         $options['main']['opendiscussion'] = true;

+ 2 - 2
tst/I18nTest.php

@@ -150,8 +150,8 @@ class I18nTest extends PHPUnit_Framework_TestCase
         $dir        = dir(PATH . 'i18n');
         $dir        = dir(PATH . 'i18n');
         while (false !== ($file = $dir->read())) {
         while (false !== ($file = $dir->read())) {
             if (strlen($file) === 7) {
             if (strlen($file) === 7) {
-                $language             = substr($file, 0, 2);
-                $languageMessageIds   = array_keys(
+                $language            = substr($file, 0, 2);
+                $languageMessageIds  = array_keys(
                     json_decode(
                     json_decode(
                         file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file),
                         file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file),
                         true
                         true

+ 1 - 1
tst/PrivateBinWithDbTest.php

@@ -35,7 +35,7 @@ class PrivateBinWithDbTest extends PrivateBinTest
         $options['model'] = array(
         $options['model'] = array(
             'class' => 'Database',
             'class' => 'Database',
         );
         );
-        $options['model_options']  = $this->_options;
+        $options['model_options'] = $this->_options;
         Helper::createIniFile(CONF, $options);
         Helper::createIniFile(CONF, $options);
     }
     }
 }
 }