(bug 35769) Convert mediawiki.Uri test suite to QUnit
authorTimo Tijhof <ttijhof@wikimedia.org>
Sat, 14 Apr 2012 07:35:18 +0000 (09:35 +0200)
committerTimo Tijhof <ttijhof@wikimedia.org>
Sat, 14 Apr 2012 07:48:21 +0000 (09:48 +0200)
- Converted ./tests/jasmine/spec/mediawiki.Uri.spec.js
  to ./tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js

- expect('').toContain('') doesn't exist in QUnit, changed to
  ok(''.indexOf('') >= 0), which is what Jasmine does internally.

- Some of the Jasmine expects()'s didn't have descriptions, added those
  while at it.

- Cleaned up the 1 test that was already in QUnit added in
  2d84f3191a9ebf18bcc1b833fd6b7d3666f91f8e.

- Cleaned up SpecRunner.html

Change-Id: Id7b498888e22576e8ff0636725191624add470d6

tests/jasmine/SpecRunner.html
tests/jasmine/spec/mediawiki.Uri.spec.js [deleted file]
tests/qunit/suites/resources/mediawiki/mediawiki.Uri.test.js

index 6af9b0c..63d0fdf 100644 (file)
@@ -1,42 +1,28 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-  "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-  <title>Jasmine Test Runner</title>
-  <link rel="stylesheet" type="text/css" href="lib/jasmine-1.0.1/jasmine.css">
-  <script type="text/javascript" src="lib/jasmine-1.0.1/jasmine.js"></script>
-  <script type="text/javascript" src="lib/jasmine-1.0.1/jasmine-html.js"></script>
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+       <head>
+               <title>Jasmine Test Runner</title>
+               <meta charset="UTF-8" />
+               <link rel="stylesheet" type="text/css" href="lib/jasmine-1.0.1/jasmine.css">
+               <script src="lib/jasmine-1.0.1/jasmine.js"></script>
+               <script src="lib/jasmine-1.0.1/jasmine-html.js"></script>
 
-  <!-- include source files here... -->
-  <script type="text/javascript" src="../../load.php?debug=true&lang=en&modules=jquery%7Cmediawiki&only=scripts&skin=vector"></script>
-  
-  <script type="text/javascript" src="../../resources/mediawiki/mediawiki.js"></script>
+               <!-- include source files here... -->
+               <script src="../../load.php?debug=true&amp;lang=en&amp;modules=startup&amp;only=scripts&amp;skin=vector&amp;*"></script>
+               <script>
+               mw.loader.load( ['mediawiki.jqueryMsg'] );
+               </script>
 
-  <script type="text/javascript" src="../../resources/mediawiki.language/mediawiki.language.js"></script>
-  <script type="text/javascript" src="../../resources/mediawiki/mediawiki.jqueryMsg.js"></script>
-  <script type="text/javascript" src="../../resources/mediawiki/mediawiki.Uri.js"></script>
-<!--
-  <script type="text/javascript" src="../../resources/mediawiki/mediawiki.api.js"></script>
-  <script type="text/javascript" src="../../resources/mediawiki/mediawiki.api.edit.js"></script>
--->
+               <!-- insert test data files here -->
+               <script src="spec/mediawiki.jqueryMsg.spec.data.js"></script>
 
-  <!-- insert test data files here -->
-  <script type="text/javascript" src="spec/mediawiki.jqueryMsg.spec.data.js"></script>
-
-  <!-- include spec files here... -->
-  <script type="text/javascript" src="spec/mediawiki.Uri.spec.js"></script>
-  <!-- 
-       <script type="text/javascript" src="spec/mw.Api.spec.js"></script>
-    <script type="text/javascript" src="spec/mw.Api.edit.spec.js"></script>
-  -->
-  <script type="text/javascript" src="spec/mediawiki.jqueryMsg.spec.js"></script>
-</head>
+               <!-- include spec files here... -->
+               <script src="spec/mediawiki.jqueryMsg.spec.js"></script>
+       </head>
 <body>
-<script type="text/javascript">
-  jasmine.getEnv().addReporter( new jasmine.TrivialReporter() );
-  jasmine.getEnv().execute();
-</script>
-
+       <script>
+               jasmine.getEnv().addReporter( new jasmine.TrivialReporter() );
+               jasmine.getEnv().execute();
+       </script>
 </body>
 </html>
diff --git a/tests/jasmine/spec/mediawiki.Uri.spec.js b/tests/jasmine/spec/mediawiki.Uri.spec.js
deleted file mode 100644 (file)
index e396ab3..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-( function() {
-
-       // ensure we have a generic URI parser if not running in a browser
-       if ( !mw.Uri ) {
-               mw.Uri = mw.UriRelative( 'http://example.com/' );
-       }
-
-       describe( "mw.Uri", function() {
-
-               describe( "should work well in loose and strict mode", function() {
-
-                       function basicTests( strict ) {
-                       
-                               describe( "should parse a simple HTTP URI correctly", function() { 
-
-                                       var uriString = 'http://www.ietf.org/rfc/rfc2396.txt';
-                                       var uri;
-                                       if ( strict ) {
-                                               uri = new mw.Uri( uriString, strict );
-                                       } else {
-                                               uri = new mw.Uri( uriString );
-                                       }
-
-                                       it( "should have basic object properties", function() {
-                                               expect( uri.protocol ).toEqual( 'http' );
-                                               expect( uri.host ).toEqual( 'www.ietf.org' );
-                                               expect( uri.port ).not.toBeDefined();
-                                               expect( uri.path ).toEqual( '/rfc/rfc2396.txt' );
-                                               expect( uri.query ).toEqual( {} );
-                                               expect( uri.fragment ).not.toBeDefined();
-                                       } );
-
-                                       describe( "should construct composite components of URI on request", function() { 
-                                               it( "should have empty userinfo", function() { 
-                                                       expect( uri.getUserInfo() ).toEqual( '' );
-                                               } );
-
-                                               it( "should have authority equal to host", function() { 
-                                                       expect( uri.getAuthority() ).toEqual( 'www.ietf.org' );
-                                               } );
-
-                                               it( "should have hostport equal to host", function() { 
-                                                       expect( uri.getHostPort() ).toEqual( 'www.ietf.org' );
-                                               } );
-
-                                               it( "should have empty string as query string", function() { 
-                                                       expect( uri.getQueryString() ).toEqual( '' );
-                                               } );
-
-                                               it( "should have path as relative path", function() { 
-                                                       expect( uri.getRelativePath() ).toEqual( '/rfc/rfc2396.txt' );
-                                               } );
-
-                                               it( "should return a uri string equivalent to original", function() { 
-                                                       expect( uri.toString() ).toEqual( uriString );
-                                               } );
-                                       } );
-                               } );
-                       }
-
-                       describe( "should work in loose mode", function() { 
-                               basicTests( false );
-                       } );
-
-                       describe( "should work in strict mode", function() {
-                               basicTests( true );
-                       } );
-
-               } );
-
-               it( "should parse a simple ftp URI correctly with user and password", function() {
-                       var uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
-                       expect( uri.protocol ).toEqual( 'ftp' );
-                       expect( uri.user ).toEqual( 'usr' );
-                       expect( uri.password ).toEqual( 'pwd' );
-                       expect( uri.host ).toEqual( '192.0.2.16' );
-                       expect( uri.port ).not.toBeDefined();
-                       expect( uri.path ).toEqual( '/' );
-                       expect( uri.query ).toEqual( {} );
-                       expect( uri.fragment ).not.toBeDefined();
-               } );
-
-               it( "should parse a simple querystring", function() {
-                       var uri = new mw.Uri( 'http://www.google.com/?q=uri' );
-                       expect( uri.protocol ).toEqual( 'http' );
-                       expect( uri.host ).toEqual( 'www.google.com' );
-                       expect( uri.port ).not.toBeDefined();
-                       expect( uri.path ).toEqual( '/' );
-                       expect( uri.query ).toBeDefined();
-                       expect( uri.query ).toEqual( { q: 'uri' } );
-                       expect( uri.fragment ).not.toBeDefined();
-                       expect( uri.getQueryString() ).toEqual( 'q=uri' );
-               } );
-
-               describe( "should handle multiple value query args (overrideKeys on)", function() {
-                       var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', { overrideKeys: true } );
-                       it ( "should parse with multiple values", function() {
-                               expect( uri.query.m ).toEqual( 'bar' );
-                               expect( uri.query.n ).toEqual( '1' );
-                       } );
-                       it ( "should accept multiple values", function() {
-                               uri.query.n = [ "x", "y", "z" ];
-                               expect( uri.toString() ).toContain( 'm=bar' );
-                               expect( uri.toString() ).toContain( 'n=x&n=y&n=z' );
-                               expect( uri.toString().length ).toEqual( 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length );
-                       } );
-               } );
-
-               describe( "should handle multiple value query args (overrideKeys off)", function() {
-                       var uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', { overrideKeys: false } );
-                       it ( "should parse with multiple values", function() {
-                               expect( uri.query.m.length ).toEqual( 2 );
-                               expect( uri.query.m[0] ).toEqual( 'foo' );
-                               expect( uri.query.m[1] ).toEqual( 'bar' );
-                               expect( uri.query.n ).toEqual( '1' );
-                       } );
-                       it ( "should accept multiple values", function() {
-                               uri.query.n = [ "x", "y", "z" ];
-                               expect( uri.toString() ).toContain( 'm=foo&m=bar' );
-                               expect( uri.toString() ).toContain( 'n=x&n=y&n=z' );
-                               expect( uri.toString().length ).toEqual( 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length );
-                       } );
-                       it ( "should be okay with removing values", function() {
-                               uri.query.m.splice( 0, 1 );
-                               delete uri.query.n;
-                               expect( uri.toString() ).toEqual( 'http://www.example.com/dir/?m=bar' );
-                               uri.query.m.splice( 0, 1 );
-                               expect( uri.toString() ).toEqual( 'http://www.example.com/dir/' );
-                       } );
-               } );
-
-               describe( "should deal with an all-dressed URI with everything", function() {
-                       var uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
-
-                       it( "should have basic object properties", function() {
-                               expect( uri.protocol ).toEqual( 'http' );
-                               expect( uri.user ).toEqual( 'auth' );
-                               expect( uri.password ).not.toBeDefined();
-                               expect( uri.host ).toEqual( 'www.example.com' );
-                               expect( uri.port ).toEqual( '81' );
-                               expect( uri.path ).toEqual( '/dir/dir.2/index.htm' );
-                               expect( uri.query ).toEqual( { q1: '0', test1: null, test2: 'value (escaped)' } );
-                               expect( uri.fragment ).toEqual( 'top' );
-                       } );
-
-                       describe( "should construct composite components of URI on request", function() { 
-                               it( "should have userinfo", function() { 
-                                       expect( uri.getUserInfo() ).toEqual( 'auth' );
-                               } );
-
-                               it( "should have authority equal to auth@hostport", function() { 
-                                       expect( uri.getAuthority() ).toEqual( 'auth@www.example.com:81' );
-                               } );
-
-                               it( "should have hostport equal to host:port", function() { 
-                                       expect( uri.getHostPort() ).toEqual( 'www.example.com:81' );
-                               } );
-
-                               it( "should have query string which contains all components", function() { 
-                                       var queryString = uri.getQueryString();
-                                       expect( queryString ).toContain( 'q1=0' );
-                                       expect( queryString ).toContain( 'test1' );
-                                       expect( queryString ).not.toContain( 'test1=' );
-                                       expect( queryString ).toContain( 'test2=value+%28escaped%29' );
-                               } );
-
-                               it( "should have path as relative path", function() { 
-                                       expect( uri.getRelativePath() ).toContain( uri.path );
-                                       expect( uri.getRelativePath() ).toContain( uri.getQueryString() );
-                                       expect( uri.getRelativePath() ).toContain( uri.fragment );
-                               } );
-
-                       } );
-               } );
-
-               describe( "should be able to clone itself", function() {
-                       var original = new mw.Uri( 'http://en.wiki.local/w/api.php?action=query&foo=bar' );                     
-                       var clone = original.clone();
-
-                       it( "should make clones equivalent", function() { 
-                               expect( original ).toEqual( clone );
-                               expect( original.toString() ).toEqual( clone.toString() );
-                       } );
-
-                       it( "should be able to manipulate clones independently", function() { 
-                               // but they are still different objects
-                               expect( original ).not.toBe( clone );
-                               // and can diverge
-                               clone.host = 'fr.wiki.local';
-                               expect( original.host ).not.toEqual( clone.host );
-                               expect( original.toString() ).not.toEqual( clone.toString() );
-                       } );
-               } );
-
-               describe( "should be able to construct URL from object", function() {
-                       it ( "should construct given basic arguments", function() {  
-                               var uri = new mw.Uri( { protocol: 'http', host: 'www.foo.local',  path: '/this' } );
-                               expect( uri.toString() ).toEqual( 'http://www.foo.local/this' );
-                       } );
-               
-                       it ( "should construct given more complex arguments", function() {  
-                               var uri = new mw.Uri( { 
-                                       protocol: 'http', 
-                                       host: 'www.foo.local',  
-                                       path: '/this', 
-                                       query: { hi: 'there' },
-                                       fragment: 'blah'  
-                               } );
-                               expect( uri.toString() ).toEqual( 'http://www.foo.local/this?hi=there#blah' );
-                       } );    
-
-                       it ( "should fail to construct without required properties", function() {  
-                               expect( function() { 
-                                       var uri = new mw.Uri( { protocol: 'http', host: 'www.foo.local' } );
-                               } ).toThrow( "Bad constructor arguments" );
-                       } );
-               } );
-
-               describe( "should be able to manipulate properties", function() { 
-                       var uri;
-
-                       beforeEach( function() { 
-                               uri = new mw.Uri( 'http://en.wiki.local/w/api.php' );                   
-                       } );
-
-                       it( "can add a fragment", function() {
-                               uri.fragment = 'frag';
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/w/api.php#frag' );
-                       } );
-
-                       it( "can change host and port", function() {
-                               uri.host = 'fr.wiki.local';
-                               uri.port = '8080';
-                               expect( uri.toString() ).toEqual( 'http://fr.wiki.local:8080/w/api.php' );
-                       } );
-
-                       it ( "can add query arguments", function() {
-                               uri.query.foo = 'bar';
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/w/api.php?foo=bar' );
-                       } );
-
-                       it ( "can extend query arguments", function() {
-                               uri.query.foo = 'bar';
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/w/api.php?foo=bar' );
-                               uri.extend( { foo: 'quux', pif: 'paf' } );
-                               expect( uri.toString() ).toContain( 'foo=quux' );
-                               expect( uri.toString() ).not.toContain( 'foo=bar' );
-                               expect( uri.toString() ).toContain( 'pif=paf' );
-                       } );
-
-                       it ( "can remove query arguments", function() {
-                               uri.query.foo = 'bar';
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/w/api.php?foo=bar' );   
-                               delete( uri.query.foo );
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/w/api.php' );   
-                       } );
-
-               } );
-
-               describe( "should handle protocol-relative URLs", function() { 
-                       var uriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
-
-                       it ( "should create protocol-relative URLs with same protocol as document", function() {
-                               var uri = new uriRel( '//en.wiki.local/w/api.php' );
-                               expect( uri.protocol ).toEqual( 'glork' );
-                       } );
-
-                       it( "should handle absolute paths by supplying protocol and host from document in loose mode", function() {
-                               var uri = new uriRel( '/foo.com' );
-                               expect( uri.toString() ).toEqual( 'glork://en.wiki.local/foo.com' );
-                       } );
-
-                       it( "should handle absolute paths by supplying host from document in loose mode", function() {
-                               var uri = new uriRel( 'http:/foo.com' );
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/foo.com' );
-                       } );
-
-                       it( "should handle absolute paths by supplying protocol and host from document in strict mode", function() {
-                               var uri = new uriRel( '/foo.com', true );
-                               expect( uri.toString() ).toEqual( 'glork://en.wiki.local/foo.com' );
-                       } );
-
-                       it( "should handle absolute paths by supplying host from document in strict mode", function() {
-                               var uri = new uriRel( 'http:/foo.com', true );
-                               expect( uri.toString() ).toEqual( 'http://en.wiki.local/foo.com' );
-                       } );
-               } );
-
-               it( "should throw error on no arguments to constructor", function() {
-                       expect( function() { 
-                               var uri = new mw.Uri();
-                       } ).toThrow( "Bad constructor arguments" );
-               } );
-
-               it( "should throw error on empty string as argument to constructor", function() {
-                       expect( function() { 
-                               var uri = new mw.Uri( '' );
-                       } ).toThrow( "Bad constructor arguments" );
-               } );
-
-               it( "should throw error on non-URI as argument to constructor", function() {
-                       expect( function() { 
-                               var uri = new mw.Uri( 'glaswegian penguins' );
-                       } ).toThrow( "Bad constructor arguments" );
-               } );
-
-               it( "should throw error on URI without protocol or // or leading / in strict mode", function() {
-                       expect( function() { 
-                               var uri = new mw.Uri( 'foo.com/bar/baz', true );
-                       } ).toThrow( "Bad constructor arguments" );
-               } );
-
-               it( "should normalize URI without protocol or // in loose mode", function() {
-                       var uri = new mw.Uri( 'foo.com/bar/baz', false );
-                       expect( uri.toString() ).toEqual( 'http://foo.com/bar/baz' );
-               } );
-
-       } );
-
-} )();
index fcdcd21..552e69e 100644 (file)
 module( 'mediawiki.Uri', QUnit.newMwEnvironment() );
 
-test( '-- Initial check', function() {
+test( '-- Initial check', function () {
+       expect( 2 );
+
+       // Ensure we have a generic URI parser if not running in a browser
+       if ( !mw.Uri ) {
+               mw.Uri = mw.UriRelative( 'http://example.com/' );
+       }
+
+       ok( mw.UriRelative, 'mw.UriRelative defined' );
+       ok( mw.Uri, 'mw.Uri defined' );
+} );
+
+$.each( [true, false], function ( i, strictMode ) {
+       test( 'Basic mw.Uri object test in ' + ( strictMode ? '' : 'non-' ) + 'strict mode for a simple HTTP URI', function () {
+               var uriString, uri;
+               expect( 2 );
+
+               uriString = 'http://www.ietf.org/rfc/rfc2396.txt';
+               uri = new mw.Uri( uriString, {
+                       strictMode: strictMode
+               });
+
+               deepEqual(
+                       {
+                               protocol: uri.protocol,
+                               host: uri.host,
+                               port: uri.port,
+                               path: uri.path,
+                               query: uri.query,
+                               fragment: uri.fragment
+                       }, {
+                               protocol: 'http',
+                               host: 'www.ietf.org',
+                               port: undefined,
+                               path: '/rfc/rfc2396.txt',
+                               query: {},
+                               fragment: undefined
+                       },
+                       'basic object properties'
+               );
+
+               deepEqual(
+                       {
+                               userInfo: uri.getUserInfo(),
+                               authority: uri.getAuthority(),
+                               hostPort: uri.getHostPort(),
+                               queryString: uri.getQueryString(),
+                               relativePath: uri.getRelativePath(),
+                               toString: uri.toString()
+                       },
+                       {
+                               userInfo: '',
+                               authority: 'www.ietf.org',
+                               hostPort: 'www.ietf.org',
+                               queryString: '',
+                               relativePath: '/rfc/rfc2396.txt',
+                               toString: uriString
+                       },
+                       'construct composite components of URI on request'
+               );
+
+       });
+});
+
+test( 'Parse an ftp URI correctly with user and password', function () {
+       var uri;
        expect( 1 );
 
-       ok( mw.UriRelative, 'mw.Uri defined' );
+       uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
+
+       deepEqual(
+               {
+                       protocol: uri.protocol,
+                       user: uri.user,
+                       password: uri.password,
+                       host: uri.host,
+                       port: uri.port,
+                       path: uri.path,
+                       query: uri.query,
+                       fragment: uri.fragment
+               },
+               {
+                       protocol: 'ftp',
+                       user: 'usr',
+                       password: 'pwd',
+                       host: '192.0.2.16',
+                       port: undefined,
+                       path: '/',
+                       query: {},
+                       fragment: undefined
+               },
+               'basic object properties'
+       );
+} );
+
+test( 'Parse a uri with simple querystring', function () {
+       var uri;
+       expect( 1 );
+
+       uri = new mw.Uri( 'http://www.google.com/?q=uri' );
+
+       deepEqual(
+               {
+                       protocol: uri.protocol,
+                       host: uri.host,
+                       port: uri.port,
+                       path: uri.path,
+                       query: uri.query,
+                       fragment: uri.fragment,
+                       queryString: uri.getQueryString()
+               },
+               {
+                       protocol: 'http',
+                       host: 'www.google.com',
+                       port: undefined,
+                       path: '/',
+                       query: { q: 'uri' },
+                       fragment: undefined,
+                       queryString: 'q=uri'
+               },
+               'basic object properties'
+       );
+} );
+
+test( 'Handle multiple query parameter (overrideKeys on)', function () {
+       var uri;
+       expect( 5 );
+
+       uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
+               overrideKeys: true
+       });
+
+       equal( uri.query.n, '1', 'multiple parameters are parsed' );
+       equal( uri.query.m, 'bar', 'last key overrides earlier keys' );
+
+       uri.query.n = [ 'x', 'y', 'z' ];
+
+       // Verify parts and total length instead of entire string because order
+       // of iteration can vary.
+       ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
+       ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
+       equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
 } );
 
-test( 'mw.Uri bug 35658', function() {
+test( 'Handle multiple query parameter (overrideKeys off)', function () {
+       var uri;
+       expect( 9 );
+
+       uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
+               overrideKeys: false
+       });
+
+       // Strict comparison so that types are also verified (n should be string '1')
+       strictEqual( uri.query.m.length, 2, 'multi-value query should be an array with 2 items' );
+       strictEqual( uri.query.m[0], 'foo', 'order and value is correct' );
+       strictEqual( uri.query.m[1], 'bar', 'order and value is correct' );
+       strictEqual( uri.query.n, '1', 'n=1 is parsed with the correct value of the expected type' );
+
+       // Change query values
+       uri.query.n = [ 'x', 'y', 'z' ];
+
+       // Verify parts and total length instead of entire string because order
+       // of iteration can vary.
+       ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
+       ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
+       equal( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
+
+       // Remove query values
+       uri.query.m.splice( 0, 1 );
+       delete uri.query.n;
+
+       equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
+
+       // Remove more query values, leaving an empty array
+       uri.query.m.splice( 0, 1 );
+       equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
+} );
+
+test( 'All-dressed URI with everything', function () {
+       var uri, queryString, relativePath;
+       expect( 11 );
+
+       uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
+
+       deepEqual(
+               {
+                       protocol: uri.protocol,
+                       user: uri.user,
+                       password: uri.password,
+                       host: uri.host,
+                       port: uri.port,
+                       path: uri.path,
+                       query: uri.query,
+                       fragment: uri.fragment
+               },
+               {
+                       protocol: 'http',
+                       user: 'auth',
+                       password: undefined,
+                       host: 'www.example.com',
+                       port: '81',
+                       path: '/dir/dir.2/index.htm',
+                       query: { q1: '0', test1: null, test2: 'value (escaped)' },
+                       fragment: 'top'
+               },
+               'basic object properties'
+       );
+
+       equal( uri.getUserInfo(), 'auth', 'user info' );
+
+       equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
+
+       equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
+
+       queryString = uri.getQueryString();
+       ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
+       ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
+       ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
+       ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
+
+       relativePath = uri.getRelativePath();
+       ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
+       ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
+       ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragement in relative path' );
+} );
+
+test( 'Cloning', function () {
+       var original, clone;
+       expect( 5 );
+
+       original = new mw.Uri( 'http://en.wiki.local/w/api.php?action=query&foo=bar' );
+       clone = original.clone();
+
+       deepEqual( clone, original, 'clone has equivalent properties' );
+       equal( original.toString(), clone.toString(), 'toString matches original' );
+
+       notStrictEqual( clone, original, 'clone is not the same when compared by reference' );
+
+       clone.host = 'fr.wiki.local';
+       notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
+       notEqual( original.toString(), clone.toString(), 'toString no longer matches original' );
+} );
+
+test( 'Constructing mw.Uri from plain object', function () {
+       var uri;
+       expect( 3 );
+
+       uri = new mw.Uri({
+               protocol: 'http',
+               host: 'www.foo.local',
+               path: '/this'
+       });
+       equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
+
+       uri = new mw.Uri({
+               protocol: 'http',
+               host: 'www.foo.local',
+               path: '/this',
+               query: { hi: 'there' },
+               fragment: 'blah'
+       });
+       equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
+
+       raises(
+               function () {
+                       var uri = new mw.Uri({
+                               protocol: 'http',
+                               host: 'www.foo.local'
+                       });
+               },
+               function ( e ) {
+                       return e.message === 'Bad constructor arguments';
+               },
+               'Construction failed when missing required properties'
+       );
+} );
+
+test( 'Manipulate properties', function () {
+       var uriBase, uri;
+       expect( 8 );
+
+       uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
+
+       uri = uriBase.clone();
+       uri.fragment = 'frag';
+       equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
+
+       uri = uriBase.clone();
+       uri.host = 'fr.wiki.local';
+       uri.port = '8080';
+       equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
+
+       uri = uriBase.clone();
+       uri.query.foo = 'bar';
+       equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
+
+       delete uri.query.foo;
+       equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
+
+       uri = uriBase.clone();
+       uri.query.foo = 'bar';
+       equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
+       uri.extend({
+               foo: 'quux',
+               pif: 'paf'
+       });
+       ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
+       ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
+       ok( uri.toString().indexOf( 'pif=paf' ) >= 0 , 'extend query arguments' );
+} );
+
+test( 'Handle protocol-relative URLs', function () {
+       var UriRel, uri;
+       expect( 5 );
+
+       UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
+
+       uri = new UriRel( '//en.wiki.local/w/api.php' );
+       equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
+
+       uri = new UriRel( '/foo.com' );
+       equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
+
+       uri = new UriRel( 'http:/foo.com' );
+       equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
+
+       uri = new UriRel( '/foo.com', true );
+       equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
+
+       uri = new UriRel( 'http:/foo.com', true );
+       equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
+} );
+
+test( 'Bad calls', function () {
+       var uri;
+       expect( 5 );
+
+       raises(
+               function () {
+                       new mw.Uri();
+               },
+               function ( e ) {
+                       return e.message === 'Bad constructor arguments';
+               },
+               'throw error on no arguments to constructor'
+       );
+
+       raises(
+               function () {
+                       new mw.Uri( '' );
+               },
+               function ( e ) {
+                       return e.message === 'Bad constructor arguments';
+               },
+               'throw error on empty string as argument to constructor'
+       );
+
+       raises(
+               function () {
+                       new mw.Uri( 'glaswegian penguins' );
+               },
+               function ( e ) {
+                       return e.message === 'Bad constructor arguments';
+               },
+               'throw error on non-URI as argument to constructor'
+       );
+
+       raises(
+               function () {
+                       new mw.Uri( 'foo.com/bar/baz', {
+                               strictMode: true
+                       });
+               },
+               function ( e ) {
+                       return e.message === 'Bad constructor arguments';
+               },
+               'throw error on URI without protocol or // or leading / in strict mode'
+       );
+
+       uri = new mw.Uri( 'foo.com/bar/baz', {
+               strictMode: false
+       });
+       equal( uri.toString(), 'http://foo.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
+});
+
+test( 'bug 35658', function () {
        expect( 2 );
 
-       var testProtocol = 'https://';
-       var testServer = 'foo.example.org';
-       var testPort = '3004';
-       var testPath = '/!1qy';
+       var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
 
-       var uriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
-       var uri = new uriClass( testPath );
-       var href = uri.toString();
+       testProtocol = 'https://';
+       testServer = 'foo.example.org';
+       testPort = '3004';
+       testPath = '/!1qy';
+
+       UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
+       uri = new UriClass( testPath );
+       href = uri.toString();
        equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
 
-       uriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
-       uri = new uriClass( testPath );
+       UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
+       uri = new UriClass( testPath );
        href = uri.toString();
        equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );