Merge "Postgres updater: add config table, adjust us_image_bits type"
authorBrion VIBBER <brion@wikimedia.org>
Wed, 4 Apr 2012 17:53:49 +0000 (17:53 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 4 Apr 2012 17:53:49 +0000 (17:53 +0000)
14 files changed:
includes/DefaultSettings.php
includes/specials/SpecialJavaScriptTest.php
maintenance/backup.inc
maintenance/backupTextPass.inc
resources/mediawiki/mediawiki.jqueryMsg.js
resources/mediawiki/mediawiki.jqueryMsg.peg
tests/TestsAutoLoader.php
tests/phpunit/StructureTest.php
tests/phpunit/maintenance/DumpTestCase.php [new file with mode: 0644]
tests/phpunit/suite.xml
tests/qunit/QUnitTestResources.php
tests/qunit/data/testrunner.js
tests/qunit/data/testwarm.inject.js [deleted file]
tests/qunit/index.html

index 1ca2aba..8402d69 100644 (file)
@@ -4233,7 +4233,17 @@ $wgEnableJavaScriptTest = false;
  */
 $wgJavaScriptTestConfig = array(
        'qunit' => array(
+               // Page where documentation can be found relevant to the QUnit test suite being ran.
+               // Used in the intro paragraph on [[Special:JavaScriptTest/qunit]] for the
+               // documentation link in the "javascripttest-qunit-intro" message.
                'documentation' => '//www.mediawiki.org/wiki/Manual:JavaScript_unit_testing',
+               // If you are submitting the QUnit test suite to a TestSwarm instance,
+               // point this to the "inject.js" script of that instance. This is was registers
+               // the QUnit hooks to extract the test results and push them back up into the
+               // TestSwarm database.
+               // @example 'http://localhost/testswarm/js/inject.js'
+               // @example '//integration.mediawiki.org/testswarm/js/inject.js'
+               'testswarm-injectjs' => false,
        ),
 );
 
index 7162393..f7e81ee 100644 (file)
@@ -138,6 +138,14 @@ class SpecialJavaScriptTest extends SpecialPage {
 HTML;
                $out->addHtml( $this->wrapSummaryHtml( $summary, 'frameworkfound' ) . $baseHtml );
 
+               // This special page is disabled by default ($wgEnableJavaScriptTest), and contains
+               // no sensitive data. In order to allow TestSwarm to embed it into a test client window,
+               // we need to allow iframing of this page.
+               $out->allowClickjacking();
+
+               // Used in ./tests/qunit/data/testrunner.js, see also documentation of
+               // $wgJavaScriptTestConfig in DefaultSettings.php
+               $out->addJsConfigVars( 'QUnitTestSwarmInjectJSPath', $wgJavaScriptTestConfig['qunit']['testswarm-injectjs'] );
        }
 
        public function isListed(){
index 470a513..6eccb26 100644 (file)
@@ -60,6 +60,15 @@ class BackupDumper {
 
        var $outputTypes = array(), $filterTypes = array();
 
+       /**
+        * The dependency-injected database to use.
+        *
+        * @var DatabaseBase|null
+        *
+        * @see self::setDb
+        */
+       protected $forcedDb = null;
+
        /**
         * @var LoadBalancer
         */
@@ -245,7 +254,10 @@ class BackupDumper {
                $table = ( $history == WikiExporter::CURRENT ) ? 'page' : 'revision';
                $field = ( $history == WikiExporter::CURRENT ) ? 'page_id' : 'rev_id';
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->forcedDb;
+               if ( $this->forcedDb === null ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+               }
                $this->maxCount = $dbr->selectField( $table, "MAX($field)", '', __METHOD__ );
                $this->startTime = wfTime();
                $this->lastTime = $this->startTime;
@@ -259,6 +271,10 @@ class BackupDumper {
         * @return DatabaseBase
         */
        function backupDb() {
+               if ( $this->forcedDb !== null ) {
+                       return $this->forcedDb;
+               }
+
                $this->lb = wfGetLBFactory()->newMainLB();
                $db = $this->lb->getConnection( DB_SLAVE, 'backup' );
 
@@ -269,6 +285,18 @@ class BackupDumper {
                return $db;
        }
 
+       /**
+        * Force the dump to use the provided database connection for database
+        * operations, wherever possible.
+        *
+        * @param $db DatabaseBase|null: (Optional) the database connection to
+        *            use. If null, resort to use the globally provided ways to
+        *            get database connections.
+        */
+       function setDb( DatabaseBase $db = null ) {
+               $this->forcedDb = $db;
+       }
+
        function __destruct() {
                if ( isset( $this->lb ) ) {
                        $this->lb->closeAll();
index bad281e..2b533ec 100644 (file)
@@ -84,6 +84,11 @@ class TextPassDumper extends BackupDumper {
                        unset( $this->lb );
                }
 
+               if ( $this->forcedDb !== null ) {
+                       $this->db = $this->forcedDb;
+                       return;
+               }
+
                if ( isset( $this->db ) && $this->db->isOpen() ) {
                        throw new MWException( 'DB is set and has not been closed by the Load Balancer' );
                }
@@ -111,15 +116,12 @@ class TextPassDumper extends BackupDumper {
        }
 
 
-       function initProgress( $history ) {
+       function initProgress( $history = WikiExporter::FULL ) {
                parent::initProgress();
                $this->timeOfCheckpoint = $this->startTime;
        }
 
        function dump( $history, $text = WikiExporter::TEXT ) {
-               // This shouldn't happen if on console... ;)
-               header( 'Content-type: text/html; charset=UTF-8' );
-
                // Notice messages will foul up your XML output even if they're
                // relatively harmless.
                if ( ini_get( 'display_errors' ) )
index 6c00bd1..a8d4a3a 100644 (file)
                                return result === null ? null : [ result[0], result[2] ];
                        }
 
+                       function templateWithOutReplacement() {
+                               var result = sequence( [
+                                       templateName,
+                                       colon,
+                                       paramExpression
+                               ] );
+                               return result === null ? null : [ result[0], result[2] ];
+                       }
+
                        var colon = makeStringParser(':');
 
                        var templateContents = choice( [
                                function() {
                                        var res = sequence( [
-                                               templateWithReplacement,
+                                               // templates can have placeholders for dynamic replacement eg: {{PLURAL:$1|one car|$1 cars}}
+                                               // or no placeholders eg: {{GRAMMAR:genitive|{{SITENAME}}}
+                                               choice( [ templateWithReplacement, templateWithOutReplacement ] ),
                                                nOrMore( 0, templateParam )
                                        ] );
                                        return res === null ? null : res[0].concat( res[1] );
index 74c57e4..e059ed1 100644 (file)
@@ -22,11 +22,15 @@ template
 
 templateContents
   = twr:templateWithReplacement p:templateParam* { return twr.concat(p) }
+  / twr:templateWithOutReplacement p:templateParam* { return twr.concat(p) }
   / t:templateName p:templateParam* { return p.length ? [ t, p ] : [ t ] }
 
 templateWithReplacement
   = t:templateName ":" r:replacement { return [ t, r ] }
 
+templateWithOutReplacement
+  = t:templateName ":" p:paramExpression { return [ t, p ] }
+
 templateParam
   = "|" e:paramExpression* { return e.length > 1 ? [ "CONCAT" ].concat(e) : e[0]; }
 
index 0c97daa..8301558 100644 (file)
@@ -25,6 +25,9 @@ $wgAutoloadClasses += array(
        //Selenium
        'SeleniumTestConstants' => "$testFolder/selenium/SeleniumTestConstants.php",
 
+       //maintenance
+       'DumpTestCase' => "$testFolder/phpunit/maintenance/DumpTestCase.php",
+
        //Generic providers
        'MediaWikiProvide' => "$testFolder/phpunit/includes/Providers.php",
 );
index 6953ab6..17ea06c 100644 (file)
@@ -20,6 +20,7 @@ class StructureTest extends MediaWikiTestCase {
                        'MediaWikiLangTestCase',
                        'MediaWikiTestCase',
                        'PHPUnit_Framework_TestCase',
+                       'DumpTestCase',
                ) );
                $testClassRegex = "^class .* extends ($testClassRegex)";
                $finder = "find $rootPath -name '*.php' '!' -name '*Test.php'" .
diff --git a/tests/phpunit/maintenance/DumpTestCase.php b/tests/phpunit/maintenance/DumpTestCase.php
new file mode 100644 (file)
index 0000000..a6fe549
--- /dev/null
@@ -0,0 +1,359 @@
+<?php
+global $IP;
+require_once( "$IP/maintenance/backup.inc" );
+
+/**
+ * Base TestCase for dumps
+ */
+abstract class DumpTestCase extends MediaWikiTestCase {
+
+       /**
+        * exception to be rethrown once in sound PHPUnit surrounding
+        *
+        * As the current MediaWikiTestCase::run is not robust enough to recover
+        * from thrown exceptions directly, we cannot throw frow within
+        * self::addDBData, although it would be appropriate. Hence, we catch the
+        * exception and store it until we are in setUp and may finally rethrow
+        * the exception without crashing the test suite.
+        *
+        * @var Exception|null
+        */
+       protected $exceptionFromAddDBData = null;
+
+       /**
+        * Holds the xmlreader used for analyzing an xml dump
+        *
+        * @var XMLReader|null
+        */
+       protected $xml = null;
+
+       /**
+        * Adds a revision to a page, while returning the resuting revision's id
+        *
+        * @param $page WikiPage: page to add the revision to
+        * @param $text string: revisions text
+        * @param $text string: revisions summare
+        *
+        * @throws MWExcepion
+        */
+       protected function addRevision( Page $page, $text, $summary ) {
+               $status = $page->doEdit( $text, $summary );
+               if ( $status->isGood() ) {
+                       $value = $status->getValue();
+                       $revision = $value['revision'];
+                       $revision_id = $revision->getId();
+                       $text_id = $revision->getTextId();
+                       if ( ( $revision_id > 0 ) && ( $text_id > 0 ) ) {
+                               return array( $revision_id, $text_id );
+                       }
+               }
+               throw new MWException( "Could not determine revision id (" . $status->getXML() . ")" );
+       }
+
+
+       /**
+        * gunzips the given file and stores the result in the original file name
+        *
+        * @param $fname string: filename to read the gzipped data from and stored
+        *             the gunzipped data into
+        */
+       protected function gunzip( $fname ) {
+               $gzipped_contents = file_get_contents( $fname );
+               if ( $gzipped_contents === FALSE ) {
+                       $this->fail( "Could not get contents of $fname" );
+               }
+               // We resort to use gzinflate instead of gzdecode, as gzdecode
+               // need not be available
+               $contents = gzinflate( substr( $gzipped_contents, 10, -8 ) );
+               $this->assertEquals( strlen( $contents ),
+                       file_put_contents( $fname, $contents ), "# bytes written" );
+       }
+
+       /**
+        * obtains a new temporary file name
+        *
+        * The obtained filename is enlisted to be removed upon tearDown
+        *
+        * @returns string: absolute name of the temporary file
+        */
+       protected function getNewTempFile() {
+               $fname = tempnam( wfTempDir(), 'MW_PHPUnit_' . get_class( $this ) . '_' );
+               $this->tmpfiles[] = $fname;
+               return $fname;
+       }
+
+       /**
+        * Default set up function.
+        *
+        * Clears $wgUser, and reports errors from addDBData to PHPUnit
+        */
+       function setUp() {
+               global $wgUser;
+
+               parent::setUp();
+
+               // Check if any Exception is stored for rethrowing from addDBData
+               // @see self::exceptionFromAddDBData
+               if ( $this->exceptionFromAddDBData !== null ) {
+                       throw $this->exceptionFromAddDBData;
+               }
+               $this->tmpfiles = array();
+
+               $wgUser = new User();
+       }
+
+       /**
+        * Default tear down function
+        *
+        * Removes all files that have been allocated via self::getNewTempFile, even if
+        * they turn out to be (empty or non-empty) directories now.
+        */
+       function tearDown() {
+               foreach ( $this->tmpfiles as $fname ) {
+                       if ( is_file( $fname ) || ( is_link( $fname ) ) ) {
+                               unlink( $fname );
+                       } elseif ( is_dir( $fname ) ) {
+                               wfRecursiveRemoveDir( $fname );
+                       }
+               }
+               parent::tearDown();
+       }
+
+
+       /**
+        * Step the current XML reader until node end of given name is found.
+        *
+        * @param $name string: name of the closing element to look for
+        *           (e.g.: "mediawiki" when looking for </mediawiki>)
+        *
+        * @return bool: true iff the end node could be found. false otherwise.
+        */
+       protected function skipToNodeEnd( $name ) {
+               while ( $this->xml->read() ) {
+                       if ( $this->xml->nodeType == XMLReader::END_ELEMENT &&
+                               $this->xml->name == $name ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Step the current XML reader to the first element start after the node
+        * end of a given name.
+        *
+        * @param $name string: name of the closing element to look for
+        *           (e.g.: "mediawiki" when looking for </mediawiki>)
+        *
+        * @return bool: true iff new element after the closing of $name could be
+        *           found. false otherwise.
+        */
+       protected function skipPastNodeEnd( $name ) {
+               $this->assertTrue( $this->skipToNodeEnd( $name ),
+                       "Skipping to end of $name" );
+               while ( $this->xml->read() ) {
+                       if ( $this->xml->nodeType == XMLReader::ELEMENT ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Opens an XML file to analyze and optionally skips past siteinfo.
+        *
+        * @param $fname string: name of file to analyze
+        * @param $skip_siteinfo bool: (optional) If true, step the xml reader
+        *           to the first element after </siteinfo>
+        */
+       protected function assertDumpStart( $fname, $skip_siteinfo = true ) {
+               $this->xml = new XMLReader();
+               $this->assertTrue( $this->xml->open( $fname ),
+                       "Opening temporary file $fname via XMLReader failed" );
+               if ( $skip_siteinfo ) {
+                       $this->assertTrue( $this->skipPastNodeEnd( "siteinfo" ),
+                               "Skipping past end of siteinfo" );
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at the final closing tag of an xml file and
+        * closes the reader.
+        *
+        * @param $tag string: (optional) the name of the final tag
+        *           (e.g.: "mediawiki" for </mediawiki>)
+        */
+       protected function assertDumpEnd( $name = "mediawiki" ) {
+               $this->assertNodeEnd( $name, false );
+               if ( $this->xml->read() ) {
+                       $this->skipWhitespace();
+               }
+               $this->assertEquals( $this->xml->nodeType, XMLReader::NONE,
+                       "No proper entity left to parse" );
+               $this->xml->close();
+       }
+
+       /**
+        * Steps the xml reader over white space
+        */
+       protected function skipWhitespace() {
+               $cont = true;
+               while ( $cont && ( ( $this->xml->nodeType == XMLReader::WHITESPACE )
+                               || ( $this->xml->nodeType == XMLReader::SIGNIFICANT_WHITESPACE ) ) ) {
+                       $cont = $this->xml->read();
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at an element of given name, and optionally
+        * skips past it.
+        *
+        * @param $name string: the name of the element to check for
+        *           (e.g.: "mediawiki" for <mediawiki>)
+        * @param $skip bool: (optional) if true, skip past the found element
+        */
+       protected function assertNodeStart( $name, $skip = true ) {
+               $this->assertEquals( $name, $this->xml->name, "Node name" );
+               $this->assertEquals( XMLReader::ELEMENT, $this->xml->nodeType, "Node type" );
+               if ( $skip ) {
+                       $this->assertTrue( $this->xml->read(), "Skipping past start tag" );
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at an closing element of given name, and optionally
+        * skips past it.
+        *
+        * @param $name string: the name of the closing element to check for
+        *           (e.g.: "mediawiki" for </mediawiki>)
+        * @param $skip bool: (optional) if true, skip past the found element
+        */
+       protected function assertNodeEnd( $name, $skip = true ) {
+               $this->assertEquals( $name, $this->xml->name, "Node name" );
+               $this->assertEquals( XMLReader::END_ELEMENT, $this->xml->nodeType, "Node type" );
+               if ( $skip ) {
+                       $this->assertTrue( $this->xml->read(), "Skipping past end tag" );
+               }
+       }
+
+
+       /**
+        * Asserts that the xml reader is at an element of given tag that contains a given text,
+        * and skips over the element.
+        *
+        * @param $name string: the name of the element to check for
+        *           (e.g.: "mediawiki" for <mediawiki>...</mediawiki>)
+        * @param $text string|false: If string, check if it equals the elements text.
+        *           If false, ignore the element's text
+        * @param $skip_ws bool: (optional) if true, skip past white spaces that trail the
+        *           closing element.
+        */
+       protected function assertTextNode( $name, $text, $skip_ws = true ) {
+               $this->assertNodeStart( $name );
+
+               if ( $text !== false ) {
+                       $this->assertEquals( $text, $this->xml->value, "Text of node " . $name );
+               }
+               $this->assertTrue( $this->xml->read(), "Skipping past processed text of " . $name );
+               $this->assertNodeEnd( $name );
+
+               if ( $skip_ws ) {
+                       $this->skipWhitespace();
+               }
+       }
+
+       /**
+        * Asserts that the xml reader is at the start of a page element and skips over the first
+        * tags, after checking them.
+        *
+        * Besides the opening page element, this function also checks for and skips over the
+        * title, ns, and id tags. Hence after this function, the xml reader is at the first
+        * revision of the current page.
+        *
+        * @param $id int: id of the page to assert
+        * @param $ns int: number of namespage to assert
+        * @param $name string: title of the current page
+        */
+       protected function assertPageStart( $id, $ns, $name ) {
+
+               $this->assertNodeStart( "page" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "title", $name );
+               $this->assertTextNode( "ns", $ns );
+               $this->assertTextNode( "id", $id );
+
+       }
+
+       /**
+        * Asserts that the xml reader is at the page's closing element and skips to the next
+        * element.
+        */
+       protected function assertPageEnd() {
+               $this->assertNodeEnd( "page" );
+               $this->skipWhitespace();
+       }
+
+       /**
+        * Asserts that the xml reader is at a revision and checks its representation before
+        * skipping over it.
+        *
+        * @param $id int: id of the revision
+        * @param $summary string: summary of the revision
+        * @param $text_id int: id of the revision's text
+        * @param $text_bytes int: # of bytes in the revision's text
+        * @param $text_sha1 string: the base36 SHA-1 of the revision's text
+        * @param $text string|false: (optional) The revision's string, or false to check for a
+        *            revision stub
+        */
+       protected function assertRevision( $id, $summary, $text_id, $text_bytes, $text_sha1, $text = false ) {
+
+               $this->assertNodeStart( "revision" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "id", $id );
+               $this->assertTextNode( "timestamp", false );
+
+               $this->assertNodeStart( "contributor" );
+               $this->skipWhitespace();
+               $this->assertTextNode( "ip", false );
+               $this->assertNodeEnd( "contributor" );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "comment", $summary );
+
+               $this->assertNodeStart( "text", false );
+               if ( $text_bytes !== false ) {
+                       $this->assertEquals( $this->xml->getAttribute( "bytes" ), $text_bytes,
+                               "Attribute 'bytes' of revision " . $id );
+               }
+
+
+               if ( $text === false ) {
+                       // Testing for a stub
+                       $this->assertEquals( $this->xml->getAttribute( "id" ), $text_id,
+                               "Text id of revision " . $id );
+                       $this->assertFalse( $this->xml->hasValue, "Revision has text" );
+                       $this->assertTrue( $this->xml->read(), "Skipping text start tag" );
+                       if ( ( $this->xml->nodeType == XMLReader::END_ELEMENT )
+                               && ( $this->xml->name == "text" ) ) {
+
+                               $this->xml->read();
+                       }
+                       $this->skipWhitespace();
+               } else {
+                       // Testing for a real dump
+                       $this->assertTrue( $this->xml->read(), "Skipping text start tag" );
+                       $this->assertEquals( $text, $this->xml->value, "Text of revision " . $id );
+                       $this->assertTrue( $this->xml->read(), "Skipping past text" );
+                       $this->assertNodeEnd( "text" );
+                       $this->skipWhitespace();
+               }
+
+               $this->assertTextNode( "sha1", $text_sha1 );
+
+               $this->assertNodeEnd( "revision" );
+               $this->skipWhitespace();
+       }
+
+}
index 8c942a4..a03f392 100644 (file)
                <testsuite name="skins">
                        <directory>skins</directory>
                </testsuite>
+               <!-- As there is a class Maintenance, we cannot use the
+                    name "maintenance" directly -->
+               <testsuite name="maintenance_suite">
+                       <directory>maintenance</directory>
+               </testsuite>
                <testsuite name="structure">
                        <file>StructureTest.php</file>
                </testsuite>
index 9f2cf8e..687ad44 100644 (file)
@@ -48,5 +48,6 @@ return array(
                        'mediawiki.special.recentchanges',
                        'mediawiki.jqueryMsg',
                ),
+               'position' => 'top',
        )
 );
index c1cce83..30ae5bc 100644 (file)
@@ -29,9 +29,14 @@ QUnit.config.urlConfig.push( 'debug' );
 /**
  *  Load TestSwarm agent
  */
-if ( QUnit.urlParams.swarmURL  ) {
-       document.write( "<scr" + "ipt src='" + QUnit.fixurl( mw.config.get( 'wgScriptPath' )
-               + '/tests/qunit/data/testwarm.inject.js' ) + "'></scr" + "ipt>" );
+// Only if the current url indicates that there is a TestSwarm instance watching us
+// (TestSwarm appends swarmURL to the test suites url it loads in iframes).
+// Otherwise this is just a simple view of Special:JavaScriptTest/qunit directly,
+// no point in loading inject.js in that case. Also, make sure that this instance
+// of MediaWiki has actually been configured with the required url to that inject.js
+// script. By default it is false.
+if ( QUnit.urlParams.swarmURL && mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) {
+       document.write( "<scr" + "ipt src='" + QUnit.fixurl( mw.config.get( 'QUnitTestSwarmInjectJSPath' ) ) + "'></scr" + "ipt>" );
 }
 
 /**
diff --git a/tests/qunit/data/testwarm.inject.js b/tests/qunit/data/testwarm.inject.js
deleted file mode 100644 (file)
index 14ee8f9..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
-       Copyright (c) 2009 John Resig
-       
-       Permission is hereby granted, free of charge, to any person
-       obtaining a copy of this software and associated documentation
-       files (the "Software"), to deal in the Software without
-       restriction, including without limitation the rights to use,
-       copy, modify, merge, publish, distribute, sublicense, and/or sell
-       copies of the Software, and to permit persons to whom the
-       Software is furnished to do so, subject to the following
-       conditions:
-       
-       The above copyright notice and this permission notice shall be
-       included in all copies or substantial portions of the Software.
-       
-       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-       EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-       OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-       NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-       HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-       WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-       FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-       OTHER DEALINGS IN THE SOFTWARE.
-
-*/
-(function(){
-
-       var DEBUG = false;
-
-       var doPost = false;
-
-       try {
-               doPost = !!window.top.postMessage;
-       } catch(e){}
-
-       var search = window.location.search,
-               url, index;
-       if( ( index = search.indexOf( "swarmURL=" ) ) != -1 )
-               url = decodeURIComponent( search.slice( index + 9 ) );
-
-       if ( !DEBUG && (!url || url.indexOf("http") !== 0) ) {
-               return;
-       }
-
-       var submitTimeout = 5;
-
-       var curHeartbeat;
-       var beatRate = 20;
-
-       // Expose the TestSwarm API
-       window.TestSwarm = {
-               submit: submit,
-               heartbeat: function(){
-                       if ( curHeartbeat ) {
-                               clearTimeout( curHeartbeat );
-                       }
-
-                       curHeartbeat = setTimeout(function(){
-                               submit({ fail: -1, total: -1 });
-                       }, beatRate * 1000);
-               },
-               serialize: function(){
-                       return trimSerialize();
-               }
-       };
-
-       // Prevent careless things from executing
-       window.print = window.confirm = window.alert = window.open = function(){};
-
-       window.onerror = function(e){
-               document.body.appendChild( document.createTextNode( "ERROR: " + e ));
-               submit({ fail: 0, error: 1, total: 1 });
-               return false;
-       };
-
-       // QUnit (jQuery)
-       // http://docs.jquery.com/QUnit
-       if ( typeof QUnit !== "undefined" ) {
-               QUnit.done = function(results){
-                       submit({
-                               fail: results.failed,
-                               error: 0,
-                               total: results.total
-                       });
-               };
-
-               QUnit.log = window.TestSwarm.heartbeat;
-               window.TestSwarm.heartbeat();
-
-               window.TestSwarm.serialize = function(){
-                       // Clean up the HTML (remove any un-needed test markup)
-                       remove("nothiddendiv");
-                       remove("loadediframe");
-                       remove("dl");
-                       remove("main");
-
-                       // Show any collapsed results
-                       var ol = document.getElementsByTagName("ol");
-                       for ( var i = 0; i < ol.length; i++ ) {
-                               ol[i].style.display = "block";
-                       }
-
-                       return trimSerialize();
-               };
-
-       // UnitTestJS (Prototype, Scriptaculous)
-       // http://github.com/tobie/unittest_js/tree/master
-       } else if ( typeof Test !== "undefined" && Test && Test.Unit && Test.Unit.runners ) {
-               var total_runners = Test.Unit.runners.length, cur_runners = 0;
-               var total = 0, fail = 0, error = 0;
-
-               for (var i = 0; i < Test.Unit.runners.length; i++) (function(i){
-                       var finish = Test.Unit.runners[i].finish;
-                       Test.Unit.runners[i].finish = function(){
-                               finish.call( this );
-
-                               var results = this.getResult();
-                               total += results.assertions;
-                               fail += results.failures;
-                               error += results.errors;
-
-                               if ( ++cur_runners === total_runners ) {
-                                       submit({
-                                               fail: fail,
-                                               error: error,
-                                               total: total
-                                       });
-                               }
-                       };
-               })(i);
-
-       // JSSpec (MooTools)
-       // http://jania.pe.kr/aw/moin.cgi/JSSpec
-       } else if ( typeof JSSpec !== "undefined" && JSSpec && JSSpec.Logger ) {
-               var onRunnerEnd = JSSpec.Logger.prototype.onRunnerEnd;
-               JSSpec.Logger.prototype.onRunnerEnd = function(){
-                       onRunnerEnd.call(this);
-
-                       // Show any collapsed results
-                       var ul = document.getElementsByTagName("ul");
-                       for ( var i = 0; i < ul.length; i++ ) {
-                               ul[i].style.display = "block";
-                       }
-
-                       submit({
-                               fail: JSSpec.runner.getTotalFailures(),
-                               error: JSSpec.runner.getTotalErrors(),
-                               total: JSSpec.runner.totalExamples
-                       });
-               };
-
-               window.TestSwarm.serialize = function(){
-                       // Show any collapsed results
-                       var ul = document.getElementsByTagName("ul");
-                       for ( var i = 0; i < ul.length; i++ ) {
-                               ul[i].style.display = "block";
-                       }
-
-                       return trimSerialize();
-               };
-
-       // JSUnit
-       // http://www.jsunit.net/
-       // Note: Injection file must be included before the frames
-       //       are document.write()d into the page.
-       } else if ( typeof JsUnitTestManager !== "undefined" ) {
-               var _done = JsUnitTestManager.prototype._done;
-               JsUnitTestManager.prototype._done = function(){
-                       _done.call(this);
-
-                       submit({
-                               fail: this.failureCount,
-                               error: this.errorCount,
-                               total: this.totalCount
-                       });
-               };
-
-               window.TestSwarm.serialize = function(){
-                       return "<pre>" + this.log.join("\n") + "</pre>";
-               };
-
-       // Selenium Core
-       // http://seleniumhq.org/projects/core/
-       } else if ( typeof SeleniumTestResult !== "undefined" && typeof LOG !== "undefined" ) {
-               // Completely overwrite the postback
-               SeleniumTestResult.prototype.post = function(){
-                       submit({
-                               fail: this.metrics.numCommandFailures,
-                               error: this.metrics.numCommandErrors,
-                               total: this.metrics.numCommandPasses + this.metrics.numCommandFailures + this.metrics.numCommandErrors
-                       });
-               };
-
-               window.TestSwarm.serialize = function(){
-                       var results = [];
-                       while ( LOG.pendingMessages.length ) {
-                               var msg = LOG.pendingMessages.shift();
-                               results.push( msg.type + ": " + msg.msg );
-                       }
-
-                       return "<pre>" + results.join("\n") + "</pre>";
-               };
-
-       // Dojo Objective Harness
-       // http://docs.dojocampus.org/quickstart/doh
-       } else if ( typeof doh !== "undefined" && doh._report ) {
-               var _report = doh._report;
-               doh._report = function(){
-                       _report.apply(this, arguments);
-
-                       submit({
-                               fail: doh._failureCount,
-                               error: doh._errorCount,
-                               total: doh._testCount
-                       });
-               };
-
-               window.TestSwarm.serialize = function(){
-                       return "<pre>" + document.getElementById("logBody").innerHTML + "</pre>";
-               };
-  // Screw.Unit
-  // git://github.com/nathansobo/screw-unit.git
-       } else if ( typeof Screw !== "undefined" && typeof jQuery !== 'undefined' && Screw && Screw.Unit ) {
-    $(Screw).bind("after", function() {
-     var passed = $('.passed').length;
-     var failed = $('.failed').length;
-     submit({
-        fail: failed,
-        error: 0,
-        total: failed + passed
-      });
-    });
-
-    $(Screw).bind("loaded", function() {
-      $('.it')
-        .bind("passed", window.TestSwarm.heartbeat)
-        .bind("failed", window.TestSwarm.heartbeat);
-      window.TestSwarm.heartbeat();
-    });
-
-    window.TestSwarm.serialize = function(){
-       return trimSerialize();
-    };
-  }
-
-       function trimSerialize(doc) {
-               doc = doc || document;
-
-               var scripts = doc.getElementsByTagName("script");
-               while ( scripts.length ) {
-                       remove( scripts[0] );
-               }
-
-               var root = window.location.href.replace(/(https?:\/\/.*?)\/.*/, "$1");
-               var cur = window.location.href.replace(/[^\/]*$/, "");
-
-               var links = doc.getElementsByTagName("link");
-               for ( var i = 0; i < links.length; i++ ) {
-                       var href = links[i].href;
-                       if ( href.indexOf("/") === 0 ) {
-                               href = root + href;
-                       } else if ( !/^https?:\/\//.test( href ) ) {
-                               href = cur + href;
-                       }
-                       links[i].href = href;
-               }
-
-               return ("<html>" + doc.documentElement.innerHTML + "</html>")
-                       .replace(/\s+/g, " ");
-       }
-
-       function remove(elem){
-               if ( typeof elem === "string" ) {
-                       elem = document.getElementById( elem );
-               }
-
-               if ( elem ) {
-                       elem.parentNode.removeChild( elem );
-               }
-       }
-
-       function submit(params){
-               if ( curHeartbeat ) {
-                       clearTimeout( curHeartbeat );
-               }
-
-               var paramItems = (url.split("?")[1] || "").split("&");
-
-               for ( var i = 0; i < paramItems.length; i++ ) {
-                       if ( paramItems[i] ) {
-                               var parts = paramItems[i].split("=");
-                               if ( !params[ parts[0] ] ) {
-                                       params[ parts[0] ] = parts[1];
-                               }
-                       }
-               }
-
-               if ( !params.state ) {
-                       params.state = "saverun";
-               }
-
-               if ( !params.results ) {
-                       params.results = window.TestSwarm.serialize();
-               }
-
-               if ( doPost ) {
-                       // Build Query String
-                       var query = "";
-
-                       for ( var i in params ) {
-                               query += ( query ? "&" : "" ) + i + "=" +
-                                       encodeURIComponent(params[i]);
-                       }
-
-                       if ( DEBUG ) {
-                               alert( query );
-                       } else {
-                               window.top.postMessage( query, "*" );
-                       }
-
-               } else {
-                       var form = document.createElement("form");
-                       form.action = url;
-                       form.method = "POST";
-
-                       for ( var i in params ) {
-                               var input = document.createElement("input");
-                               input.type = "hidden";
-                               input.name = i;
-                               input.value = params[i];
-                               form.appendChild( input );
-                       }
-
-                       if ( DEBUG ) {
-                               alert( form.innerHTML );
-                       } else {
-
-                               // Watch for the result submission timing out
-                               setTimeout(function(){
-                                       submit( params );
-                               }, submitTimeout * 1000);
-
-                               document.body.appendChild( form );
-                               form.submit();
-                       }
-               }
-       }
-
-})();
index 913c1b2..c0557ec 100644 (file)
                
                /* WikiPage specific */
                mw.config.set({"wgCanonicalNamespace": "", "wgCanonicalSpecialPageName": false, "wgNamespaceNumber": 0, "wgPageName": "Sandbox", "wgTitle": "Sandbox", "wgCurRevisionId": 486, "wgArticleId": 84, "wgIsArticle": true, "wgAction": "view", "wgUserName": null, "wgUserGroups": ["*"], "wgCategories": [], "wgBreakFrames": false, "wgPageContentLanguage": "en", "wgSeparatorTransformTable": ["", ""], "wgDigitTransformTable": ["", ""], "wgRestrictionEdit": [], "wgRestrictionMove": [], "wgRedirectedFrom": "Sandbox"});
-               
+
+               /* Special:JavaScriptTest/qunit specific */
+               mw.config.set({"QUnitTestSwarmInjectJSPath": false});
+
                /**
                 * Fix wgScriptPath and the like to the real thing,
                 * instead of fake ones (for access to /tests/qunit/data/)