Ticket #16193: 16193.diff

File 16193.diff, 50.7 KB (added by mbraak@…, 13 years ago)

qunit tests patch

  • tests/qunittests/manage.py

     
     1#!/usr/bin/env python
     2from django.core.management import execute_manager
     3import imp
     4try:
     5    imp.find_module('settings') # Assumed to be in the same directory.
     6except ImportError:
     7    import sys
     8    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
     9    sys.exit(1)
     10
     11import settings
     12
     13if __name__ == "__main__":
     14    execute_manager(settings)
  • tests/qunittests/views.py

    Property changes on: tests/qunittests/manage.py
    ___________________________________________________________________
    Added: svn:executable
       + *
    
     
     1from django.shortcuts import render_to_response
     2from django.template import RequestContext
     3from django.conf import settings
     4
     5
     6def index(request):
     7    # todo: move this to settings?
     8    javascript_files = [
     9        settings.ADMIN_MEDIA_PREFIX + 'js/core.js',
     10        settings.STATIC_URL + 'qunittests/tests.js',
     11    ]
     12
     13    return render_to_response(
     14        'index.html', {
     15            'javascript_files': javascript_files
     16        },
     17        context_instance=RequestContext(request)
     18    )
  • tests/qunittests/runtests

     
     1echo 'Go to localhost:8000 in your browser to run the tests'
     2python manage.py runserver
     3 No newline at end of file
  • tests/qunittests/settings.py

    Property changes on: tests/qunittests/runtests
    ___________________________________________________________________
    Added: svn:executable
       + *
    
     
     1DEBUG = True
     2TEMPLATE_DEBUG = DEBUG
     3
     4import os
     5BASE_DIR = os.path.dirname(__file__)
     6
     7DATABASES = {
     8    'default': {
     9        'ENGINE': 'django.db.backends.sqlite3'
     10    }
     11}
     12
     13ROOT_URLCONF = 'qunittests.urls'
     14
     15STATIC_URL = '/static/'
     16STATICFILES_DIRS = (
     17    os.path.join(BASE_DIR, 'static'),
     18)
     19
     20TEMPLATE_DIRS = (
     21    os.path.join(BASE_DIR, 'templates'),
     22)
     23
     24INSTALLED_APPS = (
     25    'django.contrib.staticfiles',
     26    'django.contrib.contenttypes',
     27    'django.contrib.admin',
     28)
  • tests/qunittests/static/qunit/qunit.css

     
     1/**
     2 * QUnit - A JavaScript Unit Testing Framework
     3 *
     4 * http://docs.jquery.com/QUnit
     5 *
     6 * Copyright (c) 2011 John Resig, Jörn Zaefferer
     7 * Dual licensed under the MIT (MIT-LICENSE.txt)
     8 * or GPL (GPL-LICENSE.txt) licenses.
     9 */
     10
     11/** Font Family and Sizes */
     12
     13#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
     14        font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
     15}
     16
     17#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
     18#qunit-tests { font-size: smaller; }
     19
     20
     21/** Resets */
     22
     23#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
     24        margin: 0;
     25        padding: 0;
     26}
     27
     28
     29/** Header */
     30
     31#qunit-header {
     32        padding: 0.5em 0 0.5em 1em;
     33
     34        color: #8699a4;
     35        background-color: #0d3349;
     36
     37        font-size: 1.5em;
     38        line-height: 1em;
     39        font-weight: normal;
     40
     41        border-radius: 15px 15px 0 0;
     42        -moz-border-radius: 15px 15px 0 0;
     43        -webkit-border-top-right-radius: 15px;
     44        -webkit-border-top-left-radius: 15px;
     45}
     46
     47#qunit-header a {
     48        text-decoration: none;
     49        color: #c2ccd1;
     50}
     51
     52#qunit-header a:hover,
     53#qunit-header a:focus {
     54        color: #fff;
     55}
     56
     57#qunit-banner {
     58        height: 5px;
     59}
     60
     61#qunit-testrunner-toolbar {
     62        padding: 0.5em 0 0.5em 2em;
     63        color: #5E740B;
     64        background-color: #eee;
     65}
     66
     67#qunit-userAgent {
     68        padding: 0.5em 0 0.5em 2.5em;
     69        background-color: #2b81af;
     70        color: #fff;
     71        text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
     72}
     73
     74
     75/** Tests: Pass/Fail */
     76
     77#qunit-tests {
     78        list-style-position: inside;
     79}
     80
     81#qunit-tests li {
     82        padding: 0.4em 0.5em 0.4em 2.5em;
     83        border-bottom: 1px solid #fff;
     84        list-style-position: inside;
     85}
     86
     87#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running  {
     88        display: none;
     89}
     90
     91#qunit-tests li strong {
     92        cursor: pointer;
     93}
     94
     95#qunit-tests li a {
     96        padding: 0.5em;
     97        color: #c2ccd1;
     98        text-decoration: none;
     99}
     100#qunit-tests li a:hover,
     101#qunit-tests li a:focus {
     102        color: #000;
     103}
     104
     105#qunit-tests ol {
     106        margin-top: 0.5em;
     107        padding: 0.5em;
     108
     109        background-color: #fff;
     110
     111        border-radius: 15px;
     112        -moz-border-radius: 15px;
     113        -webkit-border-radius: 15px;
     114
     115        box-shadow: inset 0px 2px 13px #999;
     116        -moz-box-shadow: inset 0px 2px 13px #999;
     117        -webkit-box-shadow: inset 0px 2px 13px #999;
     118}
     119
     120#qunit-tests table {
     121        border-collapse: collapse;
     122        margin-top: .2em;
     123}
     124
     125#qunit-tests th {
     126        text-align: right;
     127        vertical-align: top;
     128        padding: 0 .5em 0 0;
     129}
     130
     131#qunit-tests td {
     132        vertical-align: top;
     133}
     134
     135#qunit-tests pre {
     136        margin: 0;
     137        white-space: pre-wrap;
     138        word-wrap: break-word;
     139}
     140
     141#qunit-tests del {
     142        background-color: #e0f2be;
     143        color: #374e0c;
     144        text-decoration: none;
     145}
     146
     147#qunit-tests ins {
     148        background-color: #ffcaca;
     149        color: #500;
     150        text-decoration: none;
     151}
     152
     153/*** Test Counts */
     154
     155#qunit-tests b.counts                       { color: black; }
     156#qunit-tests b.passed                       { color: #5E740B; }
     157#qunit-tests b.failed                       { color: #710909; }
     158
     159#qunit-tests li li {
     160        margin: 0.5em;
     161        padding: 0.4em 0.5em 0.4em 0.5em;
     162        background-color: #fff;
     163        border-bottom: none;
     164        list-style-position: inside;
     165}
     166
     167/*** Passing Styles */
     168
     169#qunit-tests li li.pass {
     170        color: #5E740B;
     171        background-color: #fff;
     172        border-left: 26px solid #C6E746;
     173}
     174
     175#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
     176#qunit-tests .pass .test-name               { color: #366097; }
     177
     178#qunit-tests .pass .test-actual,
     179#qunit-tests .pass .test-expected           { color: #999999; }
     180
     181#qunit-banner.qunit-pass                    { background-color: #C6E746; }
     182
     183/*** Failing Styles */
     184
     185#qunit-tests li li.fail {
     186        color: #710909;
     187        background-color: #fff;
     188        border-left: 26px solid #EE5757;
     189}
     190
     191#qunit-tests > li:last-child {
     192        border-radius: 0 0 15px 15px;
     193        -moz-border-radius: 0 0 15px 15px;
     194        -webkit-border-bottom-right-radius: 15px;
     195        -webkit-border-bottom-left-radius: 15px;
     196}
     197
     198#qunit-tests .fail                          { color: #000000; background-color: #EE5757; }
     199#qunit-tests .fail .test-name,
     200#qunit-tests .fail .module-name             { color: #000000; }
     201
     202#qunit-tests .fail .test-actual             { color: #EE5757; }
     203#qunit-tests .fail .test-expected           { color: green;   }
     204
     205#qunit-banner.qunit-fail                    { background-color: #EE5757; }
     206
     207
     208/** Result */
     209
     210#qunit-testresult {
     211        padding: 0.5em 0.5em 0.5em 2.5em;
     212
     213        color: #2b81af;
     214        background-color: #D2E0E6;
     215
     216        border-bottom: 1px solid white;
     217}
     218
     219/** Fixture */
     220
     221#qunit-fixture {
     222        position: absolute;
     223        top: -10000px;
     224        left: -10000px;
     225}
  • tests/qunittests/static/qunit/qunit.js

     
     1/**
     2 * QUnit - A JavaScript Unit Testing Framework
     3 *
     4 * http://docs.jquery.com/QUnit
     5 *
     6 * Copyright (c) 2011 John Resig, Jörn Zaefferer
     7 * Dual licensed under the MIT (MIT-LICENSE.txt)
     8 * or GPL (GPL-LICENSE.txt) licenses.
     9 */
     10
     11(function(window) {
     12
     13var defined = {
     14        setTimeout: typeof window.setTimeout !== "undefined",
     15        sessionStorage: (function() {
     16                try {
     17                        return !!sessionStorage.getItem;
     18                } catch(e){
     19                        return false;
     20                }
     21  })()
     22};
     23
     24var testId = 0;
     25
     26var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
     27        this.name = name;
     28        this.testName = testName;
     29        this.expected = expected;
     30        this.testEnvironmentArg = testEnvironmentArg;
     31        this.async = async;
     32        this.callback = callback;
     33        this.assertions = [];
     34};
     35Test.prototype = {
     36        init: function() {
     37                var tests = id("qunit-tests");
     38                if (tests) {
     39                        var b = document.createElement("strong");
     40                                b.innerHTML = "Running " + this.name;
     41                        var li = document.createElement("li");
     42                                li.appendChild( b );
     43                                li.className = "running";
     44                                li.id = this.id = "test-output" + testId++;
     45                        tests.appendChild( li );
     46                }
     47        },
     48        setup: function() {
     49                if (this.module != config.previousModule) {
     50                        if ( config.previousModule ) {
     51                                QUnit.moduleDone( {
     52                                        name: config.previousModule,
     53                                        failed: config.moduleStats.bad,
     54                                        passed: config.moduleStats.all - config.moduleStats.bad,
     55                                        total: config.moduleStats.all
     56                                } );
     57                        }
     58                        config.previousModule = this.module;
     59                        config.moduleStats = { all: 0, bad: 0 };
     60                        QUnit.moduleStart( {
     61                                name: this.module
     62                        } );
     63                }
     64
     65                config.current = this;
     66                this.testEnvironment = extend({
     67                        setup: function() {},
     68                        teardown: function() {}
     69                }, this.moduleTestEnvironment);
     70                if (this.testEnvironmentArg) {
     71                        extend(this.testEnvironment, this.testEnvironmentArg);
     72                }
     73
     74                QUnit.testStart( {
     75                        name: this.testName
     76                } );
     77
     78                // allow utility functions to access the current test environment
     79                // TODO why??
     80                QUnit.current_testEnvironment = this.testEnvironment;
     81
     82                try {
     83                        if ( !config.pollution ) {
     84                                saveGlobal();
     85                        }
     86
     87                        this.testEnvironment.setup.call(this.testEnvironment);
     88                } catch(e) {
     89                        QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
     90                }
     91        },
     92        run: function() {
     93                if ( this.async ) {
     94                        QUnit.stop();
     95                }
     96
     97                if ( config.notrycatch ) {
     98                        this.callback.call(this.testEnvironment);
     99                        return;
     100                }
     101                try {
     102                        this.callback.call(this.testEnvironment);
     103                } catch(e) {
     104                        fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
     105                        QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
     106                        // else next test will carry the responsibility
     107                        saveGlobal();
     108
     109                        // Restart the tests if they're blocking
     110                        if ( config.blocking ) {
     111                                start();
     112                        }
     113                }
     114        },
     115        teardown: function() {
     116                try {
     117                        this.testEnvironment.teardown.call(this.testEnvironment);
     118                        checkPollution();
     119                } catch(e) {
     120                        QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
     121                }
     122        },
     123        finish: function() {
     124                if ( this.expected && this.expected != this.assertions.length ) {
     125                        QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
     126                }
     127
     128                var good = 0, bad = 0,
     129                        tests = id("qunit-tests");
     130
     131                config.stats.all += this.assertions.length;
     132                config.moduleStats.all += this.assertions.length;
     133
     134                if ( tests ) {
     135                        var ol  = document.createElement("ol");
     136
     137                        for ( var i = 0; i < this.assertions.length; i++ ) {
     138                                var assertion = this.assertions[i];
     139
     140                                var li = document.createElement("li");
     141                                li.className = assertion.result ? "pass" : "fail";
     142                                li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
     143                                ol.appendChild( li );
     144
     145                                if ( assertion.result ) {
     146                                        good++;
     147                                } else {
     148                                        bad++;
     149                                        config.stats.bad++;
     150                                        config.moduleStats.bad++;
     151                                }
     152                        }
     153
     154                        // store result when possible
     155                        if ( QUnit.config.reorder && defined.sessionStorage ) {
     156                                if (bad) {
     157                                        sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
     158                                } else {
     159                                        sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
     160                                }
     161                        }
     162
     163                        if (bad == 0) {
     164                                ol.style.display = "none";
     165                        }
     166
     167                        var b = document.createElement("strong");
     168                        b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
     169
     170                        var a = document.createElement("a");
     171                        a.innerHTML = "Rerun";
     172                        a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
     173
     174                        addEvent(b, "click", function() {
     175                                var next = b.nextSibling.nextSibling,
     176                                        display = next.style.display;
     177                                next.style.display = display === "none" ? "block" : "none";
     178                        });
     179
     180                        addEvent(b, "dblclick", function(e) {
     181                                var target = e && e.target ? e.target : window.event.srcElement;
     182                                if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
     183                                        target = target.parentNode;
     184                                }
     185                                if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
     186                                        window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
     187                                }
     188                        });
     189
     190                        var li = id(this.id);
     191                        li.className = bad ? "fail" : "pass";
     192                        li.removeChild( li.firstChild );
     193                        li.appendChild( b );
     194                        li.appendChild( a );
     195                        li.appendChild( ol );
     196
     197                } else {
     198                        for ( var i = 0; i < this.assertions.length; i++ ) {
     199                                if ( !this.assertions[i].result ) {
     200                                        bad++;
     201                                        config.stats.bad++;
     202                                        config.moduleStats.bad++;
     203                                }
     204                        }
     205                }
     206
     207                try {
     208                        QUnit.reset();
     209                } catch(e) {
     210                        fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
     211                }
     212
     213                QUnit.testDone( {
     214                        name: this.testName,
     215                        failed: bad,
     216                        passed: this.assertions.length - bad,
     217                        total: this.assertions.length
     218                } );
     219        },
     220
     221        queue: function() {
     222                var test = this;
     223                synchronize(function() {
     224                        test.init();
     225                });
     226                function run() {
     227                        // each of these can by async
     228                        synchronize(function() {
     229                                test.setup();
     230                        });
     231                        synchronize(function() {
     232                                test.run();
     233                        });
     234                        synchronize(function() {
     235                                test.teardown();
     236                        });
     237                        synchronize(function() {
     238                                test.finish();
     239                        });
     240                }
     241                // defer when previous test run passed, if storage is available
     242                var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
     243                if (bad) {
     244                        run();
     245                } else {
     246                        synchronize(run);
     247                };
     248        }
     249
     250};
     251
     252var QUnit = {
     253
     254        // call on start of module test to prepend name to all tests
     255        module: function(name, testEnvironment) {
     256                config.currentModule = name;
     257                config.currentModuleTestEnviroment = testEnvironment;
     258        },
     259
     260        asyncTest: function(testName, expected, callback) {
     261                if ( arguments.length === 2 ) {
     262                        callback = expected;
     263                        expected = 0;
     264                }
     265
     266                QUnit.test(testName, expected, callback, true);
     267        },
     268
     269        test: function(testName, expected, callback, async) {
     270                var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
     271
     272                if ( arguments.length === 2 ) {
     273                        callback = expected;
     274                        expected = null;
     275                }
     276                // is 2nd argument a testEnvironment?
     277                if ( expected && typeof expected === 'object') {
     278                        testEnvironmentArg =  expected;
     279                        expected = null;
     280                }
     281
     282                if ( config.currentModule ) {
     283                        name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
     284                }
     285
     286                if ( !validTest(config.currentModule + ": " + testName) ) {
     287                        return;
     288                }
     289
     290                var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
     291                test.module = config.currentModule;
     292                test.moduleTestEnvironment = config.currentModuleTestEnviroment;
     293                test.queue();
     294        },
     295
     296        /**
     297         * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
     298         */
     299        expect: function(asserts) {
     300                config.current.expected = asserts;
     301        },
     302
     303        /**
     304         * Asserts true.
     305         * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
     306         */
     307        ok: function(a, msg) {
     308                a = !!a;
     309                var details = {
     310                        result: a,
     311                        message: msg
     312                };
     313                msg = escapeHtml(msg);
     314                QUnit.log(details);
     315                config.current.assertions.push({
     316                        result: a,
     317                        message: msg
     318                });
     319        },
     320
     321        /**
     322         * Checks that the first two arguments are equal, with an optional message.
     323         * Prints out both actual and expected values.
     324         *
     325         * Prefered to ok( actual == expected, message )
     326         *
     327         * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
     328         *
     329         * @param Object actual
     330         * @param Object expected
     331         * @param String message (optional)
     332         */
     333        equal: function(actual, expected, message) {
     334                QUnit.push(expected == actual, actual, expected, message);
     335        },
     336
     337        notEqual: function(actual, expected, message) {
     338                QUnit.push(expected != actual, actual, expected, message);
     339        },
     340
     341        deepEqual: function(actual, expected, message) {
     342                QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
     343        },
     344
     345        notDeepEqual: function(actual, expected, message) {
     346                QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
     347        },
     348
     349        strictEqual: function(actual, expected, message) {
     350                QUnit.push(expected === actual, actual, expected, message);
     351        },
     352
     353        notStrictEqual: function(actual, expected, message) {
     354                QUnit.push(expected !== actual, actual, expected, message);
     355        },
     356
     357        raises: function(block, expected, message) {
     358                var actual, ok = false;
     359
     360                if (typeof expected === 'string') {
     361                        message = expected;
     362                        expected = null;
     363                }
     364
     365                try {
     366                        block();
     367                } catch (e) {
     368                        actual = e;
     369                }
     370
     371                if (actual) {
     372                        // we don't want to validate thrown error
     373                        if (!expected) {
     374                                ok = true;
     375                        // expected is a regexp
     376                        } else if (QUnit.objectType(expected) === "regexp") {
     377                                ok = expected.test(actual);
     378                        // expected is a constructor
     379                        } else if (actual instanceof expected) {
     380                                ok = true;
     381                        // expected is a validation function which returns true is validation passed
     382                        } else if (expected.call({}, actual) === true) {
     383                                ok = true;
     384                        }
     385                }
     386
     387                QUnit.ok(ok, message);
     388        },
     389
     390        start: function() {
     391                config.semaphore--;
     392                if (config.semaphore > 0) {
     393                        // don't start until equal number of stop-calls
     394                        return;
     395                }
     396                if (config.semaphore < 0) {
     397                        // ignore if start is called more often then stop
     398                        config.semaphore = 0;
     399                }
     400                // A slight delay, to avoid any current callbacks
     401                if ( defined.setTimeout ) {
     402                        window.setTimeout(function() {
     403                                if ( config.timeout ) {
     404                                        clearTimeout(config.timeout);
     405                                }
     406
     407                                config.blocking = false;
     408                                process();
     409                        }, 13);
     410                } else {
     411                        config.blocking = false;
     412                        process();
     413                }
     414        },
     415
     416        stop: function(timeout) {
     417                config.semaphore++;
     418                config.blocking = true;
     419
     420                if ( timeout && defined.setTimeout ) {
     421                        clearTimeout(config.timeout);
     422                        config.timeout = window.setTimeout(function() {
     423                                QUnit.ok( false, "Test timed out" );
     424                                QUnit.start();
     425                        }, timeout);
     426                }
     427        }
     428};
     429
     430// Backwards compatibility, deprecated
     431QUnit.equals = QUnit.equal;
     432QUnit.same = QUnit.deepEqual;
     433
     434// Maintain internal state
     435var config = {
     436        // The queue of tests to run
     437        queue: [],
     438
     439        // block until document ready
     440        blocking: true,
     441
     442        // by default, run previously failed tests first
     443        // very useful in combination with "Hide passed tests" checked
     444        reorder: true,
     445
     446        noglobals: false,
     447        notrycatch: false
     448};
     449
     450// Load paramaters
     451(function() {
     452        var location = window.location || { search: "", protocol: "file:" },
     453                params = location.search.slice( 1 ).split( "&" ),
     454                length = params.length,
     455                urlParams = {},
     456                current;
     457
     458        if ( params[ 0 ] ) {
     459                for ( var i = 0; i < length; i++ ) {
     460                        current = params[ i ].split( "=" );
     461                        current[ 0 ] = decodeURIComponent( current[ 0 ] );
     462                        // allow just a key to turn on a flag, e.g., test.html?noglobals
     463                        current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
     464                        urlParams[ current[ 0 ] ] = current[ 1 ];
     465                        if ( current[ 0 ] in config ) {
     466                                config[ current[ 0 ] ] = current[ 1 ];
     467                        }
     468                }
     469        }
     470
     471        QUnit.urlParams = urlParams;
     472        config.filter = urlParams.filter;
     473
     474        // Figure out if we're running the tests from a server or not
     475        QUnit.isLocal = !!(location.protocol === 'file:');
     476})();
     477
     478// Expose the API as global variables, unless an 'exports'
     479// object exists, in that case we assume we're in CommonJS
     480if ( typeof exports === "undefined" || typeof require === "undefined" ) {
     481        extend(window, QUnit);
     482        window.QUnit = QUnit;
     483} else {
     484        extend(exports, QUnit);
     485        exports.QUnit = QUnit;
     486}
     487
     488// define these after exposing globals to keep them in these QUnit namespace only
     489extend(QUnit, {
     490        config: config,
     491
     492        // Initialize the configuration options
     493        init: function() {
     494                extend(config, {
     495                        stats: { all: 0, bad: 0 },
     496                        moduleStats: { all: 0, bad: 0 },
     497                        started: +new Date,
     498                        updateRate: 1000,
     499                        blocking: false,
     500                        autostart: true,
     501                        autorun: false,
     502                        filter: "",
     503                        queue: [],
     504                        semaphore: 0
     505                });
     506
     507                var tests = id( "qunit-tests" ),
     508                        banner = id( "qunit-banner" ),
     509                        result = id( "qunit-testresult" );
     510
     511                if ( tests ) {
     512                        tests.innerHTML = "";
     513                }
     514
     515                if ( banner ) {
     516                        banner.className = "";
     517                }
     518
     519                if ( result ) {
     520                        result.parentNode.removeChild( result );
     521                }
     522
     523                if ( tests ) {
     524                        result = document.createElement( "p" );
     525                        result.id = "qunit-testresult";
     526                        result.className = "result";
     527                        tests.parentNode.insertBefore( result, tests );
     528                        result.innerHTML = 'Running...<br/>&nbsp;';
     529                }
     530        },
     531
     532        /**
     533         * Resets the test setup. Useful for tests that modify the DOM.
     534         *
     535         * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
     536         */
     537        reset: function() {
     538                if ( window.jQuery ) {
     539                        jQuery( "#qunit-fixture" ).html( config.fixture );
     540                } else {
     541                        var main = id( 'qunit-fixture' );
     542                        if ( main ) {
     543                                main.innerHTML = config.fixture;
     544                        }
     545                }
     546        },
     547
     548        /**
     549         * Trigger an event on an element.
     550         *
     551         * @example triggerEvent( document.body, "click" );
     552         *
     553         * @param DOMElement elem
     554         * @param String type
     555         */
     556        triggerEvent: function( elem, type, event ) {
     557                if ( document.createEvent ) {
     558                        event = document.createEvent("MouseEvents");
     559                        event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
     560                                0, 0, 0, 0, 0, false, false, false, false, 0, null);
     561                        elem.dispatchEvent( event );
     562
     563                } else if ( elem.fireEvent ) {
     564                        elem.fireEvent("on"+type);
     565                }
     566        },
     567
     568        // Safe object type checking
     569        is: function( type, obj ) {
     570                return QUnit.objectType( obj ) == type;
     571        },
     572
     573        objectType: function( obj ) {
     574                if (typeof obj === "undefined") {
     575                                return "undefined";
     576
     577                // consider: typeof null === object
     578                }
     579                if (obj === null) {
     580                                return "null";
     581                }
     582
     583                var type = Object.prototype.toString.call( obj )
     584                        .match(/^\[object\s(.*)\]$/)[1] || '';
     585
     586                switch (type) {
     587                                case 'Number':
     588                                                if (isNaN(obj)) {
     589                                                                return "nan";
     590                                                } else {
     591                                                                return "number";
     592                                                }
     593                                case 'String':
     594                                case 'Boolean':
     595                                case 'Array':
     596                                case 'Date':
     597                                case 'RegExp':
     598                                case 'Function':
     599                                                return type.toLowerCase();
     600                }
     601                if (typeof obj === "object") {
     602                                return "object";
     603                }
     604                return undefined;
     605        },
     606
     607        push: function(result, actual, expected, message) {
     608                var details = {
     609                        result: result,
     610                        message: message,
     611                        actual: actual,
     612                        expected: expected
     613                };
     614
     615                message = escapeHtml(message) || (result ? "okay" : "failed");
     616                message = '<span class="test-message">' + message + "</span>";
     617                expected = escapeHtml(QUnit.jsDump.parse(expected));
     618                actual = escapeHtml(QUnit.jsDump.parse(actual));
     619                var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
     620                if (actual != expected) {
     621                        output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
     622                        output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
     623                }
     624                if (!result) {
     625                        var source = sourceFromStacktrace();
     626                        if (source) {
     627                                details.source = source;
     628                                output += '<tr class="test-source"><th>Source: </th><td><pre>' + source +'</pre></td></tr>';
     629                        }
     630                }
     631                output += "</table>";
     632
     633                QUnit.log(details);
     634
     635                config.current.assertions.push({
     636                        result: !!result,
     637                        message: output
     638                });
     639        },
     640
     641        url: function( params ) {
     642                params = extend( extend( {}, QUnit.urlParams ), params );
     643                var querystring = "?",
     644                        key;
     645                for ( key in params ) {
     646                        querystring += encodeURIComponent( key ) + "=" +
     647                                encodeURIComponent( params[ key ] ) + "&";
     648                }
     649                return window.location.pathname + querystring.slice( 0, -1 );
     650        },
     651
     652        // Logging callbacks; all receive a single argument with the listed properties
     653        // run test/logs.html for any related changes
     654        begin: function() {},
     655        // done: { failed, passed, total, runtime }
     656        done: function() {},
     657        // log: { result, actual, expected, message }
     658        log: function() {},
     659        // testStart: { name }
     660        testStart: function() {},
     661        // testDone: { name, failed, passed, total }
     662        testDone: function() {},
     663        // moduleStart: { name }
     664        moduleStart: function() {},
     665        // moduleDone: { name, failed, passed, total }
     666        moduleDone: function() {}
     667});
     668
     669if ( typeof document === "undefined" || document.readyState === "complete" ) {
     670        config.autorun = true;
     671}
     672
     673addEvent(window, "load", function() {
     674        QUnit.begin({});
     675
     676        // Initialize the config, saving the execution queue
     677        var oldconfig = extend({}, config);
     678        QUnit.init();
     679        extend(config, oldconfig);
     680
     681        config.blocking = false;
     682
     683        var userAgent = id("qunit-userAgent");
     684        if ( userAgent ) {
     685                userAgent.innerHTML = navigator.userAgent;
     686        }
     687        var banner = id("qunit-header");
     688        if ( banner ) {
     689                banner.innerHTML = '<a href="' + QUnit.url({ filter: undefined }) + '"> ' + banner.innerHTML + '</a> ' +
     690                        '<label><input name="noglobals" type="checkbox"' + ( config.noglobals ? ' checked="checked"' : '' ) + '>noglobals</label>' +
     691                        '<label><input name="notrycatch" type="checkbox"' + ( config.notrycatch ? ' checked="checked"' : '' ) + '>notrycatch</label>';
     692                addEvent( banner, "change", function( event ) {
     693                        var params = {};
     694                        params[ event.target.name ] = event.target.checked ? true : undefined;
     695                        window.location = QUnit.url( params );
     696                });
     697        }
     698
     699        var toolbar = id("qunit-testrunner-toolbar");
     700        if ( toolbar ) {
     701                var filter = document.createElement("input");
     702                filter.type = "checkbox";
     703                filter.id = "qunit-filter-pass";
     704                addEvent( filter, "click", function() {
     705                        var ol = document.getElementById("qunit-tests");
     706                        if ( filter.checked ) {
     707                                ol.className = ol.className + " hidepass";
     708                        } else {
     709                                var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
     710                                ol.className = tmp.replace(/ hidepass /, " ");
     711                        }
     712                        if ( defined.sessionStorage ) {
     713                                if (filter.checked) {
     714                                        sessionStorage.setItem("qunit-filter-passed-tests",  "true");
     715                                } else {
     716                                        sessionStorage.removeItem("qunit-filter-passed-tests");
     717                                }
     718                        }
     719                });
     720                if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) {
     721                        filter.checked = true;
     722                        var ol = document.getElementById("qunit-tests");
     723                        ol.className = ol.className + " hidepass";
     724                }
     725                toolbar.appendChild( filter );
     726
     727                var label = document.createElement("label");
     728                label.setAttribute("for", "qunit-filter-pass");
     729                label.innerHTML = "Hide passed tests";
     730                toolbar.appendChild( label );
     731        }
     732
     733        var main = id('qunit-fixture');
     734        if ( main ) {
     735                config.fixture = main.innerHTML;
     736        }
     737
     738        if (config.autostart) {
     739                QUnit.start();
     740        }
     741});
     742
     743function done() {
     744        config.autorun = true;
     745
     746        // Log the last module results
     747        if ( config.currentModule ) {
     748                QUnit.moduleDone( {
     749                        name: config.currentModule,
     750                        failed: config.moduleStats.bad,
     751                        passed: config.moduleStats.all - config.moduleStats.bad,
     752                        total: config.moduleStats.all
     753                } );
     754        }
     755
     756        var banner = id("qunit-banner"),
     757                tests = id("qunit-tests"),
     758                runtime = +new Date - config.started,
     759                passed = config.stats.all - config.stats.bad,
     760                html = [
     761                        'Tests completed in ',
     762                        runtime,
     763                        ' milliseconds.<br/>',
     764                        '<span class="passed">',
     765                        passed,
     766                        '</span> tests of <span class="total">',
     767                        config.stats.all,
     768                        '</span> passed, <span class="failed">',
     769                        config.stats.bad,
     770                        '</span> failed.'
     771                ].join('');
     772
     773        if ( banner ) {
     774                banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
     775        }
     776
     777        if ( tests ) {
     778                id( "qunit-testresult" ).innerHTML = html;
     779        }
     780
     781        if ( typeof document !== "undefined" && document.title ) {
     782                // show ✖ for good, ✔ for bad suite result in title
     783                // use escape sequences in case file gets loaded with non-utf-8-charset
     784                document.title = (config.stats.bad ? "\u2716" : "\u2714") + " " + document.title;
     785        }
     786
     787        QUnit.done( {
     788                failed: config.stats.bad,
     789                passed: passed,
     790                total: config.stats.all,
     791                runtime: runtime
     792        } );
     793}
     794
     795function validTest( name ) {
     796        var filter = config.filter,
     797                run = false;
     798
     799        if ( !filter ) {
     800                return true;
     801        }
     802
     803        var not = filter.charAt( 0 ) === "!";
     804        if ( not ) {
     805                filter = filter.slice( 1 );
     806        }
     807
     808        if ( name.indexOf( filter ) !== -1 ) {
     809                return !not;
     810        }
     811
     812        if ( not ) {
     813                run = true;
     814        }
     815
     816        return run;
     817}
     818
     819// so far supports only Firefox, Chrome and Opera (buggy)
     820// could be extended in the future to use something like https://github.com/csnover/TraceKit
     821function sourceFromStacktrace() {
     822        try {
     823                throw new Error();
     824        } catch ( e ) {
     825                if (e.stacktrace) {
     826                        // Opera
     827                        return e.stacktrace.split("\n")[6];
     828                } else if (e.stack) {
     829                        // Firefox, Chrome
     830                        return e.stack.split("\n")[4];
     831                }
     832        }
     833}
     834
     835function escapeHtml(s) {
     836        if (!s) {
     837                return "";
     838        }
     839        s = s + "";
     840        return s.replace(/[\&"<>\\]/g, function(s) {
     841                switch(s) {
     842                        case "&": return "&amp;";
     843                        case "\\": return "\\\\";
     844                        case '"': return '\"';
     845                        case "<": return "&lt;";
     846                        case ">": return "&gt;";
     847                        default: return s;
     848                }
     849        });
     850}
     851
     852function synchronize( callback ) {
     853        config.queue.push( callback );
     854
     855        if ( config.autorun && !config.blocking ) {
     856                process();
     857        }
     858}
     859
     860function process() {
     861        var start = (new Date()).getTime();
     862
     863        while ( config.queue.length && !config.blocking ) {
     864                if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
     865                        config.queue.shift()();
     866                } else {
     867                        window.setTimeout( process, 13 );
     868                        break;
     869                }
     870        }
     871  if (!config.blocking && !config.queue.length) {
     872    done();
     873  }
     874}
     875
     876function saveGlobal() {
     877        config.pollution = [];
     878
     879        if ( config.noglobals ) {
     880                for ( var key in window ) {
     881                        config.pollution.push( key );
     882                }
     883        }
     884}
     885
     886function checkPollution( name ) {
     887        var old = config.pollution;
     888        saveGlobal();
     889
     890        var newGlobals = diff( config.pollution, old );
     891        if ( newGlobals.length > 0 ) {
     892                ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
     893        }
     894
     895        var deletedGlobals = diff( old, config.pollution );
     896        if ( deletedGlobals.length > 0 ) {
     897                ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
     898        }
     899}
     900
     901// returns a new Array with the elements that are in a but not in b
     902function diff( a, b ) {
     903        var result = a.slice();
     904        for ( var i = 0; i < result.length; i++ ) {
     905                for ( var j = 0; j < b.length; j++ ) {
     906                        if ( result[i] === b[j] ) {
     907                                result.splice(i, 1);
     908                                i--;
     909                                break;
     910                        }
     911                }
     912        }
     913        return result;
     914}
     915
     916function fail(message, exception, callback) {
     917        if ( typeof console !== "undefined" && console.error && console.warn ) {
     918                console.error(message);
     919                console.error(exception);
     920                console.warn(callback.toString());
     921
     922        } else if ( window.opera && opera.postError ) {
     923                opera.postError(message, exception, callback.toString);
     924        }
     925}
     926
     927function extend(a, b) {
     928        for ( var prop in b ) {
     929                if ( b[prop] === undefined ) {
     930                        delete a[prop];
     931                } else {
     932                        a[prop] = b[prop];
     933                }
     934        }
     935
     936        return a;
     937}
     938
     939function addEvent(elem, type, fn) {
     940        if ( elem.addEventListener ) {
     941                elem.addEventListener( type, fn, false );
     942        } else if ( elem.attachEvent ) {
     943                elem.attachEvent( "on" + type, fn );
     944        } else {
     945                fn();
     946        }
     947}
     948
     949function id(name) {
     950        return !!(typeof document !== "undefined" && document && document.getElementById) &&
     951                document.getElementById( name );
     952}
     953
     954// Test for equality any JavaScript type.
     955// Discussions and reference: http://philrathe.com/articles/equiv
     956// Test suites: http://philrathe.com/tests/equiv
     957// Author: Philippe Rathé <prathe@gmail.com>
     958QUnit.equiv = function () {
     959
     960    var innerEquiv; // the real equiv function
     961    var callers = []; // stack to decide between skip/abort functions
     962    var parents = []; // stack to avoiding loops from circular referencing
     963
     964    // Call the o related callback with the given arguments.
     965    function bindCallbacks(o, callbacks, args) {
     966        var prop = QUnit.objectType(o);
     967        if (prop) {
     968            if (QUnit.objectType(callbacks[prop]) === "function") {
     969                return callbacks[prop].apply(callbacks, args);
     970            } else {
     971                return callbacks[prop]; // or undefined
     972            }
     973        }
     974    }
     975
     976    var callbacks = function () {
     977
     978        // for string, boolean, number and null
     979        function useStrictEquality(b, a) {
     980            if (b instanceof a.constructor || a instanceof b.constructor) {
     981                // to catch short annotaion VS 'new' annotation of a declaration
     982                // e.g. var i = 1;
     983                //      var j = new Number(1);
     984                return a == b;
     985            } else {
     986                return a === b;
     987            }
     988        }
     989
     990        return {
     991            "string": useStrictEquality,
     992            "boolean": useStrictEquality,
     993            "number": useStrictEquality,
     994            "null": useStrictEquality,
     995            "undefined": useStrictEquality,
     996
     997            "nan": function (b) {
     998                return isNaN(b);
     999            },
     1000
     1001            "date": function (b, a) {
     1002                return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
     1003            },
     1004
     1005            "regexp": function (b, a) {
     1006                return QUnit.objectType(b) === "regexp" &&
     1007                    a.source === b.source && // the regex itself
     1008                    a.global === b.global && // and its modifers (gmi) ...
     1009                    a.ignoreCase === b.ignoreCase &&
     1010                    a.multiline === b.multiline;
     1011            },
     1012
     1013            // - skip when the property is a method of an instance (OOP)
     1014            // - abort otherwise,
     1015            //   initial === would have catch identical references anyway
     1016            "function": function () {
     1017                var caller = callers[callers.length - 1];
     1018                return caller !== Object &&
     1019                        typeof caller !== "undefined";
     1020            },
     1021
     1022            "array": function (b, a) {
     1023                var i, j, loop;
     1024                var len;
     1025
     1026                // b could be an object literal here
     1027                if ( ! (QUnit.objectType(b) === "array")) {
     1028                    return false;
     1029                }
     1030
     1031                len = a.length;
     1032                if (len !== b.length) { // safe and faster
     1033                    return false;
     1034                }
     1035
     1036                //track reference to avoid circular references
     1037                parents.push(a);
     1038                for (i = 0; i < len; i++) {
     1039                    loop = false;
     1040                    for(j=0;j<parents.length;j++){
     1041                        if(parents[j] === a[i]){
     1042                            loop = true;//dont rewalk array
     1043                        }
     1044                    }
     1045                    if (!loop && ! innerEquiv(a[i], b[i])) {
     1046                        parents.pop();
     1047                        return false;
     1048                    }
     1049                }
     1050                parents.pop();
     1051                return true;
     1052            },
     1053
     1054            "object": function (b, a) {
     1055                var i, j, loop;
     1056                var eq = true; // unless we can proove it
     1057                var aProperties = [], bProperties = []; // collection of strings
     1058
     1059                // comparing constructors is more strict than using instanceof
     1060                if ( a.constructor !== b.constructor) {
     1061                    return false;
     1062                }
     1063
     1064                // stack constructor before traversing properties
     1065                callers.push(a.constructor);
     1066                //track reference to avoid circular references
     1067                parents.push(a);
     1068
     1069                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
     1070                    loop = false;
     1071                    for(j=0;j<parents.length;j++){
     1072                        if(parents[j] === a[i])
     1073                            loop = true; //don't go down the same path twice
     1074                    }
     1075                    aProperties.push(i); // collect a's properties
     1076
     1077                    if (!loop && ! innerEquiv(a[i], b[i])) {
     1078                        eq = false;
     1079                        break;
     1080                    }
     1081                }
     1082
     1083                callers.pop(); // unstack, we are done
     1084                parents.pop();
     1085
     1086                for (i in b) {
     1087                    bProperties.push(i); // collect b's properties
     1088                }
     1089
     1090                // Ensures identical properties name
     1091                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
     1092            }
     1093        };
     1094    }();
     1095
     1096    innerEquiv = function () { // can take multiple arguments
     1097        var args = Array.prototype.slice.apply(arguments);
     1098        if (args.length < 2) {
     1099            return true; // end transition
     1100        }
     1101
     1102        return (function (a, b) {
     1103            if (a === b) {
     1104                return true; // catch the most you can
     1105            } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
     1106                return false; // don't lose time with error prone cases
     1107            } else {
     1108                return bindCallbacks(a, callbacks, [b, a]);
     1109            }
     1110
     1111        // apply transition with (1..n) arguments
     1112        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
     1113    };
     1114
     1115    return innerEquiv;
     1116
     1117}();
     1118
     1119/**
     1120 * jsDump
     1121 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
     1122 * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
     1123 * Date: 5/15/2008
     1124 * @projectDescription Advanced and extensible data dumping for Javascript.
     1125 * @version 1.0.0
     1126 * @author Ariel Flesler
     1127 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
     1128 */
     1129QUnit.jsDump = (function() {
     1130        function quote( str ) {
     1131                return '"' + str.toString().replace(/"/g, '\\"') + '"';
     1132        };
     1133        function literal( o ) {
     1134                return o + '';
     1135        };
     1136        function join( pre, arr, post ) {
     1137                var s = jsDump.separator(),
     1138                        base = jsDump.indent(),
     1139                        inner = jsDump.indent(1);
     1140                if ( arr.join )
     1141                        arr = arr.join( ',' + s + inner );
     1142                if ( !arr )
     1143                        return pre + post;
     1144                return [ pre, inner + arr, base + post ].join(s);
     1145        };
     1146        function array( arr ) {
     1147                var i = arr.length,     ret = Array(i);
     1148                this.up();
     1149                while ( i-- )
     1150                        ret[i] = this.parse( arr[i] );
     1151                this.down();
     1152                return join( '[', ret, ']' );
     1153        };
     1154
     1155        var reName = /^function (\w+)/;
     1156
     1157        var jsDump = {
     1158                parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
     1159                        var     parser = this.parsers[ type || this.typeOf(obj) ];
     1160                        type = typeof parser;
     1161
     1162                        return type == 'function' ? parser.call( this, obj ) :
     1163                                   type == 'string' ? parser :
     1164                                   this.parsers.error;
     1165                },
     1166                typeOf:function( obj ) {
     1167                        var type;
     1168                        if ( obj === null ) {
     1169                                type = "null";
     1170                        } else if (typeof obj === "undefined") {
     1171                                type = "undefined";
     1172                        } else if (QUnit.is("RegExp", obj)) {
     1173                                type = "regexp";
     1174                        } else if (QUnit.is("Date", obj)) {
     1175                                type = "date";
     1176                        } else if (QUnit.is("Function", obj)) {
     1177                                type = "function";
     1178                        } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
     1179                                type = "window";
     1180                        } else if (obj.nodeType === 9) {
     1181                                type = "document";
     1182                        } else if (obj.nodeType) {
     1183                                type = "node";
     1184                        } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
     1185                                type = "array";
     1186                        } else {
     1187                                type = typeof obj;
     1188                        }
     1189                        return type;
     1190                },
     1191                separator:function() {
     1192                        return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
     1193                },
     1194                indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
     1195                        if ( !this.multiline )
     1196                                return '';
     1197                        var chr = this.indentChar;
     1198                        if ( this.HTML )
     1199                                chr = chr.replace(/\t/g,'   ').replace(/ /g,'&nbsp;');
     1200                        return Array( this._depth_ + (extra||0) ).join(chr);
     1201                },
     1202                up:function( a ) {
     1203                        this._depth_ += a || 1;
     1204                },
     1205                down:function( a ) {
     1206                        this._depth_ -= a || 1;
     1207                },
     1208                setParser:function( name, parser ) {
     1209                        this.parsers[name] = parser;
     1210                },
     1211                // The next 3 are exposed so you can use them
     1212                quote:quote,
     1213                literal:literal,
     1214                join:join,
     1215                //
     1216                _depth_: 1,
     1217                // This is the list of parsers, to modify them, use jsDump.setParser
     1218                parsers:{
     1219                        window: '[Window]',
     1220                        document: '[Document]',
     1221                        error:'[ERROR]', //when no parser is found, shouldn't happen
     1222                        unknown: '[Unknown]',
     1223                        'null':'null',
     1224                        'undefined':'undefined',
     1225                        'function':function( fn ) {
     1226                                var ret = 'function',
     1227                                        name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
     1228                                if ( name )
     1229                                        ret += ' ' + name;
     1230                                ret += '(';
     1231
     1232                                ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
     1233                                return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
     1234                        },
     1235                        array: array,
     1236                        nodelist: array,
     1237                        arguments: array,
     1238                        object:function( map ) {
     1239                                var ret = [ ];
     1240                                QUnit.jsDump.up();
     1241                                for ( var key in map )
     1242                                        ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
     1243                                QUnit.jsDump.down();
     1244                                return join( '{', ret, '}' );
     1245                        },
     1246                        node:function( node ) {
     1247                                var open = QUnit.jsDump.HTML ? '&lt;' : '<',
     1248                                        close = QUnit.jsDump.HTML ? '&gt;' : '>';
     1249
     1250                                var tag = node.nodeName.toLowerCase(),
     1251                                        ret = open + tag;
     1252
     1253                                for ( var a in QUnit.jsDump.DOMAttrs ) {
     1254                                        var val = node[QUnit.jsDump.DOMAttrs[a]];
     1255                                        if ( val )
     1256                                                ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
     1257                                }
     1258                                return ret + close + open + '/' + tag + close;
     1259                        },
     1260                        functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
     1261                                var l = fn.length;
     1262                                if ( !l ) return '';
     1263
     1264                                var args = Array(l);
     1265                                while ( l-- )
     1266                                        args[l] = String.fromCharCode(97+l);//97 is 'a'
     1267                                return ' ' + args.join(', ') + ' ';
     1268                        },
     1269                        key:quote, //object calls it internally, the key part of an item in a map
     1270                        functionCode:'[code]', //function calls it internally, it's the content of the function
     1271                        attribute:quote, //node calls it internally, it's an html attribute value
     1272                        string:quote,
     1273                        date:quote,
     1274                        regexp:literal, //regex
     1275                        number:literal,
     1276                        'boolean':literal
     1277                },
     1278                DOMAttrs:{//attributes to dump from nodes, name=>realName
     1279                        id:'id',
     1280                        name:'name',
     1281                        'class':'className'
     1282                },
     1283                HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
     1284                indentChar:'  ',//indentation unit
     1285                multiline:true //if true, items in a collection, are separated by a \n, else just a space.
     1286        };
     1287
     1288        return jsDump;
     1289})();
     1290
     1291// from Sizzle.js
     1292function getText( elems ) {
     1293        var ret = "", elem;
     1294
     1295        for ( var i = 0; elems[i]; i++ ) {
     1296                elem = elems[i];
     1297
     1298                // Get the text from text nodes and CDATA nodes
     1299                if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
     1300                        ret += elem.nodeValue;
     1301
     1302                // Traverse everything else, except comment nodes
     1303                } else if ( elem.nodeType !== 8 ) {
     1304                        ret += getText( elem.childNodes );
     1305                }
     1306        }
     1307
     1308        return ret;
     1309};
     1310
     1311/*
     1312 * Javascript Diff Algorithm
     1313 *  By John Resig (http://ejohn.org/)
     1314 *  Modified by Chu Alan "sprite"
     1315 *
     1316 * Released under the MIT license.
     1317 *
     1318 * More Info:
     1319 *  http://ejohn.org/projects/javascript-diff-algorithm/
     1320 *
     1321 * Usage: QUnit.diff(expected, actual)
     1322 *
     1323 * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
     1324 */
     1325QUnit.diff = (function() {
     1326        function diff(o, n){
     1327                var ns = new Object();
     1328                var os = new Object();
     1329
     1330                for (var i = 0; i < n.length; i++) {
     1331                        if (ns[n[i]] == null)
     1332                                ns[n[i]] = {
     1333                                        rows: new Array(),
     1334                                        o: null
     1335                                };
     1336                        ns[n[i]].rows.push(i);
     1337                }
     1338
     1339                for (var i = 0; i < o.length; i++) {
     1340                        if (os[o[i]] == null)
     1341                                os[o[i]] = {
     1342                                        rows: new Array(),
     1343                                        n: null
     1344                                };
     1345                        os[o[i]].rows.push(i);
     1346                }
     1347
     1348                for (var i in ns) {
     1349                        if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
     1350                                n[ns[i].rows[0]] = {
     1351                                        text: n[ns[i].rows[0]],
     1352                                        row: os[i].rows[0]
     1353                                };
     1354                                o[os[i].rows[0]] = {
     1355                                        text: o[os[i].rows[0]],
     1356                                        row: ns[i].rows[0]
     1357                                };
     1358                        }
     1359                }
     1360
     1361                for (var i = 0; i < n.length - 1; i++) {
     1362                        if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
     1363                        n[i + 1] == o[n[i].row + 1]) {
     1364                                n[i + 1] = {
     1365                                        text: n[i + 1],
     1366                                        row: n[i].row + 1
     1367                                };
     1368                                o[n[i].row + 1] = {
     1369                                        text: o[n[i].row + 1],
     1370                                        row: i + 1
     1371                                };
     1372                        }
     1373                }
     1374
     1375                for (var i = n.length - 1; i > 0; i--) {
     1376                        if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
     1377                        n[i - 1] == o[n[i].row - 1]) {
     1378                                n[i - 1] = {
     1379                                        text: n[i - 1],
     1380                                        row: n[i].row - 1
     1381                                };
     1382                                o[n[i].row - 1] = {
     1383                                        text: o[n[i].row - 1],
     1384                                        row: i - 1
     1385                                };
     1386                        }
     1387                }
     1388
     1389                return {
     1390                        o: o,
     1391                        n: n
     1392                };
     1393        }
     1394
     1395        return function(o, n){
     1396                o = o.replace(/\s+$/, '');
     1397                n = n.replace(/\s+$/, '');
     1398                var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
     1399
     1400                var str = "";
     1401
     1402                var oSpace = o.match(/\s+/g);
     1403                if (oSpace == null) {
     1404                        oSpace = [" "];
     1405                }
     1406                else {
     1407                        oSpace.push(" ");
     1408                }
     1409                var nSpace = n.match(/\s+/g);
     1410                if (nSpace == null) {
     1411                        nSpace = [" "];
     1412                }
     1413                else {
     1414                        nSpace.push(" ");
     1415                }
     1416
     1417                if (out.n.length == 0) {
     1418                        for (var i = 0; i < out.o.length; i++) {
     1419                                str += '<del>' + out.o[i] + oSpace[i] + "</del>";
     1420                        }
     1421                }
     1422                else {
     1423                        if (out.n[0].text == null) {
     1424                                for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
     1425                                        str += '<del>' + out.o[n] + oSpace[n] + "</del>";
     1426                                }
     1427                        }
     1428
     1429                        for (var i = 0; i < out.n.length; i++) {
     1430                                if (out.n[i].text == null) {
     1431                                        str += '<ins>' + out.n[i] + nSpace[i] + "</ins>";
     1432                                }
     1433                                else {
     1434                                        var pre = "";
     1435
     1436                                        for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
     1437                                                pre += '<del>' + out.o[n] + oSpace[n] + "</del>";
     1438                                        }
     1439                                        str += " " + out.n[i].text + nSpace[i] + pre;
     1440                                }
     1441                        }
     1442                }
     1443
     1444                return str;
     1445        };
     1446})();
     1447
     1448})(this);
  • tests/qunittests/static/qunittests/tests.js

     
     1module('admin.core.date');
     2
     3test('Date.getCorrectYear', function() {
     4    // jan 1 2011 returns 2011
     5    equal(
     6        new Date(2011, 0, 1).getCorrectYear(),
     7        2011,
     8        'jan 1 2011'
     9    );
     10
     11    // jan 1 1899 returns 1999
     12    // This is strange, but expected behaviour at this moment.
     13    equal(
     14        new Date(1899, 0, 1).getCorrectYear(),
     15        1999,
     16        'jan 1 1899'
     17    );
     18
     19    // jan 1 1911 returns 2011
     20    equal(
     21        new Date(1911, 0, 1).getCorrectYear(),
     22        2011,
     23        'jan 1 1911'
     24    );
     25});
     26
     27test('Date.getTwelveHours', function() {
     28    // 0:00 -> 12
     29    equal(
     30        new Date(2011, 0, 1, 0, 0).getTwelveHours(),
     31        12,
     32        '0:00'
     33    );
     34
     35    // 11:00 -> 11
     36    equal(
     37        new Date(2011, 0, 1, 11, 0).getTwelveHours(),
     38        11,
     39        '11:00'
     40    );
     41
     42    // 16:00 -> 4
     43    equal(
     44        new Date(2011, 0, 1, 16, 0).getTwelveHours(),
     45        4,
     46        '16:00'
     47    );
     48});
     49
     50test('Date.getTwoDigitMonth', function() {
     51    // jan 1
     52    equal(
     53        new Date(2011, 0, 1).getTwoDigitMonth(),
     54        '01',
     55        'jan 1'
     56    );
     57
     58    // oct 1
     59    equal(
     60        new Date(2011, 9, 1).getTwoDigitMonth(),
     61        '10',
     62        'oct 1'
     63    );
     64});
     65
     66test('Date.getTwoDigitDate', function() {
     67    // jan 1
     68    equal(
     69        new Date(2011, 0, 1).getTwoDigitDate(),
     70        '01',
     71        'jan 1'
     72    );
     73
     74    // jan 15
     75    equal(
     76        new Date(2011, 0, 15).getTwoDigitDate(),
     77        '15',
     78        'jan 15'
     79    );
     80});
     81
     82test('Date.getTwoDigitTwelveHour', function() {
     83    // 0:00 -> '12'
     84    equal(
     85        new Date(2011, 0, 1, 0, 0).getTwoDigitTwelveHour(),
     86        '12',
     87        '0:00'
     88    );
     89
     90    // 4:00
     91    equal(
     92        new Date(2011, 0, 1, 4, 0).getTwoDigitTwelveHour(),
     93        '04',
     94        '4:00'
     95    );
     96
     97    // 22:00
     98    equal(
     99        new Date(2011, 0, 1, 22, 0).getTwoDigitTwelveHour(),
     100        '10',
     101        '22:00'
     102    );
     103});
     104
     105// todo: add more tests
  • tests/qunittests/urls.py

     
     1from django.conf.urls.defaults import patterns, include, url
     2from django.contrib import admin
     3from django.contrib.staticfiles.urls import staticfiles_urlpatterns
     4
     5import views
     6
     7
     8admin.autodiscover()
     9
     10
     11urlpatterns = patterns('',
     12    url(r'^$', views.index),
     13    url(r'^admin/', include(admin.site.urls)),
     14)
     15
     16urlpatterns += staticfiles_urlpatterns()
  • tests/qunittests/README

     
     1Unit tests for javascript.
     2
     3Usage:
     4$ python manage.py runserver
     5
     6Go to localhost:8000 in your browser to run the tests
     7
     8Uses qunit lib. See http://docs.jquery.com/Qunit
     9
     10Todo:
     11Add license file for qunit. Also check if the license is compatible.
     12
     13To add a test:
     14- Edit the file static/qunittests/tests.js.
     15- If you need to add a javascript file, edit 'javascript_files' in views.py.
  • tests/qunittests/templates/index.html

     
     1<!doctype html>
     2<html lang="en">
     3<head>
     4        <meta charset="utf-8">
     5        <title>Django unit tests</title>
     6        <link rel="stylesheet" href="{{ STATIC_URL }}qunit/qunit.css">
     7        <script src="{{ STATIC_URL }}qunit/qunit.js"></script>
     8        {% for file in javascript_files %}
     9          <script src="{{ file }}"></script>
     10        {% endfor %}
     11</head>
     12<body>
     13        <h1 id="qunit-header">Django unit tests</h1>
     14        <h2 id="qunit-banner"></h2>
     15        <h2 id="qunit-userAgent"></h2>
     16        <ol id="qunit-tests">
     17        </ol>
     18</body>
     19</html>
     20 No newline at end of file
Back to Top