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