Import: Handle uploads with sha1 starting with 0 properly
[mediawiki.git] / tests / qunit / suites / resources / mediawiki / mediawiki.Uri.test.js
blobb12803d6c773ac42be6b3f588f36168a2b9a4609
1 /*jshint -W024 */
2 ( function ( mw, $ ) {
3         QUnit.module( 'mediawiki.Uri', QUnit.newMwEnvironment( {
4                 setup: function () {
5                         this.mwUriOrg = mw.Uri;
6                         mw.Uri = mw.UriRelative( 'http://example.org/w/index.php' );
7                 },
8                 teardown: function () {
9                         mw.Uri = this.mwUriOrg;
10                         delete this.mwUriOrg;
11                 }
12         } ) );
14         $.each( [ true, false ], function ( i, strictMode ) {
15                 QUnit.test( 'Basic construction and properties (' + ( strictMode ? '' : 'non-' ) + 'strict mode)', 2, function ( assert ) {
16                         var uriString, uri;
17                         uriString = 'http://www.ietf.org/rfc/rfc2396.txt';
18                         uri = new mw.Uri( uriString, {
19                                 strictMode: strictMode
20                         } );
22                         assert.deepEqual(
23                                 {
24                                         protocol: uri.protocol,
25                                         host: uri.host,
26                                         port: uri.port,
27                                         path: uri.path,
28                                         query: uri.query,
29                                         fragment: uri.fragment
30                                 }, {
31                                         protocol: 'http',
32                                         host: 'www.ietf.org',
33                                         port: undefined,
34                                         path: '/rfc/rfc2396.txt',
35                                         query: {},
36                                         fragment: undefined
37                                 },
38                                 'basic object properties'
39                         );
41                         assert.deepEqual(
42                                 {
43                                         userInfo: uri.getUserInfo(),
44                                         authority: uri.getAuthority(),
45                                         hostPort: uri.getHostPort(),
46                                         queryString: uri.getQueryString(),
47                                         relativePath: uri.getRelativePath(),
48                                         toString: uri.toString()
49                                 },
50                                 {
51                                         userInfo: '',
52                                         authority: 'www.ietf.org',
53                                         hostPort: 'www.ietf.org',
54                                         queryString: '',
55                                         relativePath: '/rfc/rfc2396.txt',
56                                         toString: uriString
57                                 },
58                                 'construct composite components of URI on request'
59                         );
60                 } );
61         } );
63         QUnit.test( 'Constructor( String[, Object ] )', 11, function ( assert ) {
64                 var uri;
66                 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
67                         overrideKeys: true
68                 } );
70                 // Strict comparison to assert that numerical values stay strings
71                 assert.strictEqual( uri.query.n, '1', 'Simple parameter with overrideKeys:true' );
72                 assert.strictEqual( uri.query.m, 'bar', 'Last key overrides earlier keys with overrideKeys:true' );
74                 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
75                         overrideKeys: false
76                 } );
78                 assert.strictEqual( uri.query.n, '1', 'Simple parameter with overrideKeys:false' );
79                 assert.strictEqual( uri.query.m[ 0 ], 'foo', 'Order of multi-value parameters with overrideKeys:true' );
80                 assert.strictEqual( uri.query.m[ 1 ], 'bar', 'Order of multi-value parameters with overrideKeys:true' );
81                 assert.strictEqual( uri.query.m.length, 2, 'Number of mult-value field is correct' );
83                 uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
85                 assert.deepEqual(
86                         {
87                                 protocol: uri.protocol,
88                                 user: uri.user,
89                                 password: uri.password,
90                                 host: uri.host,
91                                 port: uri.port,
92                                 path: uri.path,
93                                 query: uri.query,
94                                 fragment: uri.fragment
95                         },
96                         {
97                                 protocol: 'ftp',
98                                 user: 'usr',
99                                 password: 'pwd',
100                                 host: '192.0.2.16',
101                                 port: undefined,
102                                 path: '/',
103                                 query: {},
104                                 fragment: undefined
105                         },
106                         'Parse an ftp URI correctly with user and password'
107                 );
109                 assert.throws(
110                         function () {
111                                 return new mw.Uri( 'glaswegian penguins' );
112                         },
113                         function ( e ) {
114                                 return e.message === 'Bad constructor arguments';
115                         },
116                         'throw error on non-URI as argument to constructor'
117                 );
119                 assert.throws(
120                         function () {
121                                 return new mw.Uri( 'example.com/bar/baz', {
122                                         strictMode: true
123                                 } );
124                         },
125                         function ( e ) {
126                                 return e.message === 'Bad constructor arguments';
127                         },
128                         'throw error on URI without protocol or // or leading / in strict mode'
129                 );
131                 uri = new mw.Uri( 'example.com/bar/baz', {
132                         strictMode: false
133                 } );
134                 assert.equal( uri.toString(), 'http://example.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
136                 /*jshint -W001 */
137                 uri = new mw.Uri( 'http://example.com/index.php?key=key&hasOwnProperty=hasOwnProperty&constructor=constructor&watch=watch' );
138                 assert.deepEqual(
139                         uri.query,
140                         {
141                                 key: 'key',
142                                 constructor: 'constructor',
143                                 hasOwnProperty: 'hasOwnProperty',
144                                 watch: 'watch'
145                         },
146                         'Keys in query strings support names of Object prototypes (bug T114344)'
147                 );
148                 /*jshint +W001 */
149         } );
151         QUnit.test( 'Constructor( Object )', 3, function ( assert ) {
152                 var uri = new mw.Uri( {
153                         protocol: 'http',
154                         host: 'www.foo.local',
155                         path: '/this'
156                 } );
157                 assert.equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
159                 uri = new mw.Uri( {
160                         protocol: 'http',
161                         host: 'www.foo.local',
162                         path: '/this',
163                         query: { hi: 'there' },
164                         fragment: 'blah'
165                 } );
166                 assert.equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
168                 assert.throws(
169                         function () {
170                                 return new mw.Uri( {
171                                         protocol: 'http',
172                                         host: 'www.foo.local'
173                                 } );
174                         },
175                         function ( e ) {
176                                 return e.message === 'Bad constructor arguments';
177                         },
178                         'Construction failed when missing required properties'
179                 );
180         } );
182         QUnit.test( 'Constructor( empty )', 4, function ( assert ) {
183                 var testuri, MyUri, uri;
185                 testuri = 'http://example.org/w/index.php';
186                 MyUri = mw.UriRelative( testuri );
188                 uri = new MyUri();
189                 assert.equal( uri.toString(), testuri, 'no arguments' );
191                 uri = new MyUri( undefined );
192                 assert.equal( uri.toString(), testuri, 'undefined' );
194                 uri = new MyUri( null );
195                 assert.equal( uri.toString(), testuri, 'null' );
197                 uri = new MyUri( '' );
198                 assert.equal( uri.toString(), testuri, 'empty string' );
199         } );
201         QUnit.test( 'Properties', 8, function ( assert ) {
202                 var uriBase, uri;
204                 uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
206                 uri = uriBase.clone();
207                 uri.fragment = 'frag';
208                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
210                 uri = uriBase.clone();
211                 uri.host = 'fr.wiki.local';
212                 uri.port = '8080';
213                 assert.equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
215                 uri = uriBase.clone();
216                 uri.query.foo = 'bar';
217                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
219                 delete uri.query.foo;
220                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
222                 uri = uriBase.clone();
223                 uri.query.foo = 'bar';
224                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
225                 uri.extend( {
226                         foo: 'quux',
227                         pif: 'paf'
228                 } );
229                 assert.ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
230                 assert.ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
231                 assert.ok( uri.toString().indexOf( 'pif=paf' ) >= 0, 'extend query arguments' );
232         } );
234         QUnit.test( '.getQueryString()', 2, function ( assert ) {
235                 var uri = new mw.Uri( 'http://search.example.com/?q=uri' );
237                 assert.deepEqual(
238                         {
239                                 protocol: uri.protocol,
240                                 host: uri.host,
241                                 port: uri.port,
242                                 path: uri.path,
243                                 query: uri.query,
244                                 fragment: uri.fragment,
245                                 queryString: uri.getQueryString()
246                         },
247                         {
248                                 protocol: 'http',
249                                 host: 'search.example.com',
250                                 port: undefined,
251                                 path: '/',
252                                 query: { q: 'uri' },
253                                 fragment: undefined,
254                                 queryString: 'q=uri'
255                         },
256                         'basic object properties'
257                 );
259                 uri = new mw.Uri( 'https://example.com/mw/index.php?title=Sandbox/7&other=Sandbox/7&foo' );
260                 assert.equal(
261                         uri.getQueryString(),
262                         'title=Sandbox/7&other=Sandbox%2F7&foo',
263                         'title parameter is escaped the wiki-way'
264                 );
266         } );
268         QUnit.test( '.clone()', 6, function ( assert ) {
269                 var original, clone;
271                 original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
272                 clone = original.clone();
274                 assert.deepEqual( clone, original, 'clone has equivalent properties' );
275                 assert.equal( original.toString(), clone.toString(), 'toString matches original' );
277                 assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
279                 clone.host = 'bar.example.org';
280                 assert.notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
281                 assert.notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
283                 clone.query.three = 3;
285                 assert.deepEqual(
286                         original.query,
287                         { one: '1', two: '2' },
288                         'Properties is deep cloned (bug 37708)'
289                 );
290         } );
292         QUnit.test( '.toString() after query manipulation', 8, function ( assert ) {
293                 var uri;
295                 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
296                         overrideKeys: true
297                 } );
299                 uri.query.n = [ 'x', 'y', 'z' ];
301                 // Verify parts and total length instead of entire string because order
302                 // of iteration can vary.
303                 assert.ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
304                 assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
305                 assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
307                 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
308                         overrideKeys: false
309                 } );
311                 // Change query values
312                 uri.query.n = [ 'x', 'y', 'z' ];
314                 // Verify parts and total length instead of entire string because order
315                 // of iteration can vary.
316                 assert.ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
317                 assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
318                 assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
320                 // Remove query values
321                 uri.query.m.splice( 0, 1 );
322                 delete uri.query.n;
324                 assert.equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
326                 // Remove more query values, leaving an empty array
327                 uri.query.m.splice( 0, 1 );
328                 assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
329         } );
331         QUnit.test( 'Variable defaultUri', 2, function ( assert ) {
332                 var uri,
333                         href = 'http://example.org/w/index.php#here',
334                         UriClass = mw.UriRelative( function () {
335                                 return href;
336                         } );
338                 uri = new UriClass();
339                 assert.deepEqual(
340                         {
341                                 protocol: uri.protocol,
342                                 user: uri.user,
343                                 password: uri.password,
344                                 host: uri.host,
345                                 port: uri.port,
346                                 path: uri.path,
347                                 query: uri.query,
348                                 fragment: uri.fragment
349                         },
350                         {
351                                 protocol: 'http',
352                                 user: undefined,
353                                 password: undefined,
354                                 host: 'example.org',
355                                 port: undefined,
356                                 path: '/w/index.php',
357                                 query: {},
358                                 fragment: 'here'
359                         },
360                         'basic object properties'
361                 );
363                 // Default URI may change, e.g. via history.replaceState, pushState or location.hash (T74334)
364                 href = 'https://example.com/wiki/Foo?v=2';
365                 uri = new UriClass();
366                 assert.deepEqual(
367                         {
368                                 protocol: uri.protocol,
369                                 user: uri.user,
370                                 password: uri.password,
371                                 host: uri.host,
372                                 port: uri.port,
373                                 path: uri.path,
374                                 query: uri.query,
375                                 fragment: uri.fragment
376                         },
377                         {
378                                 protocol: 'https',
379                                 user: undefined,
380                                 password: undefined,
381                                 host: 'example.com',
382                                 port: undefined,
383                                 path: '/wiki/Foo',
384                                 query: { v: '2' },
385                                 fragment: undefined
386                         },
387                         'basic object properties'
388                 );
389         } );
391         QUnit.test( 'Advanced URL', 11, function ( assert ) {
392                 var uri, queryString, relativePath;
394                 uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
396                 assert.deepEqual(
397                         {
398                                 protocol: uri.protocol,
399                                 user: uri.user,
400                                 password: uri.password,
401                                 host: uri.host,
402                                 port: uri.port,
403                                 path: uri.path,
404                                 query: uri.query,
405                                 fragment: uri.fragment
406                         },
407                         {
408                                 protocol: 'http',
409                                 user: 'auth',
410                                 password: undefined,
411                                 host: 'www.example.com',
412                                 port: '81',
413                                 path: '/dir/dir.2/index.htm',
414                                 query: { q1: '0', test1: null, test2: 'value (escaped)' },
415                                 fragment: 'top'
416                         },
417                         'basic object properties'
418                 );
420                 assert.equal( uri.getUserInfo(), 'auth', 'user info' );
422                 assert.equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
424                 assert.equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
426                 queryString = uri.getQueryString();
427                 assert.ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
428                 assert.ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
429                 assert.ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
430                 assert.ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
432                 relativePath = uri.getRelativePath();
433                 assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
434                 assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
435                 assert.ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragment in relative path' );
436         } );
438         QUnit.test( 'Parse a uri with an @ symbol in the path and query', 1, function ( assert ) {
439                 var uri = new mw.Uri( 'http://www.example.com/test@test?x=@uri&y@=uri&z@=@' );
441                 assert.deepEqual(
442                         {
443                                 protocol: uri.protocol,
444                                 user: uri.user,
445                                 password: uri.password,
446                                 host: uri.host,
447                                 port: uri.port,
448                                 path: uri.path,
449                                 query: uri.query,
450                                 fragment: uri.fragment,
451                                 queryString: uri.getQueryString()
452                         },
453                         {
454                                 protocol: 'http',
455                                 user: undefined,
456                                 password: undefined,
457                                 host: 'www.example.com',
458                                 port: undefined,
459                                 path: '/test@test',
460                                 query: { x: '@uri', 'y@': 'uri', 'z@': '@' },
461                                 fragment: undefined,
462                                 queryString: 'x=%40uri&y%40=uri&z%40=%40'
463                         },
464                         'basic object properties'
465                 );
466         } );
468         QUnit.test( 'Handle protocol-relative URLs', 5, function ( assert ) {
469                 var UriRel, uri;
471                 UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
473                 uri = new UriRel( '//en.wiki.local/w/api.php' );
474                 assert.equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
476                 uri = new UriRel( '/foo.com' );
477                 assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
479                 uri = new UriRel( 'http:/foo.com' );
480                 assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
482                 uri = new UriRel( '/foo.com', true );
483                 assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
485                 uri = new UriRel( 'http:/foo.com', true );
486                 assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
487         } );
489         QUnit.test( 'bug 35658', 2, function ( assert ) {
490                 var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
492                 testProtocol = 'https://';
493                 testServer = 'foo.example.org';
494                 testPort = '3004';
495                 testPath = '/!1qy';
497                 UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
498                 uri = new UriClass( testPath );
499                 href = uri.toString();
500                 assert.equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
502                 UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
503                 uri = new UriClass( testPath );
504                 href = uri.toString();
505                 assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
506         } );
507 }( mediaWiki, jQuery ) );