Merge "Fix French special pages titles inconsistencies"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki / mediawiki.Uri.test.js
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 } ) );
13
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 } );
21
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 );
40
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 } );
62
63 QUnit.test( 'Constructor( String[, Object ] )', 11, function ( assert ) {
64 var uri;
65
66 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
67 overrideKeys: true
68 } );
69
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' );
73
74 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
75 overrideKeys: false
76 } );
77
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' );
82
83 uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
84
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 );
108
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 );
118
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 );
130
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' );
135
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 } );
150
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' );
158
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' );
167
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 } );
181
182 QUnit.test( 'Constructor( empty )', 4, function ( assert ) {
183 var testuri, MyUri, uri;
184
185 testuri = 'http://example.org/w/index.php';
186 MyUri = mw.UriRelative( testuri );
187
188 uri = new MyUri();
189 assert.equal( uri.toString(), testuri, 'no arguments' );
190
191 uri = new MyUri( undefined );
192 assert.equal( uri.toString(), testuri, 'undefined' );
193
194 uri = new MyUri( null );
195 assert.equal( uri.toString(), testuri, 'null' );
196
197 uri = new MyUri( '' );
198 assert.equal( uri.toString(), testuri, 'empty string' );
199 } );
200
201 QUnit.test( 'Properties', 8, function ( assert ) {
202 var uriBase, uri;
203
204 uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
205
206 uri = uriBase.clone();
207 uri.fragment = 'frag';
208 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
209
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' );
214
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' );
218
219 delete uri.query.foo;
220 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
221
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 } );
233
234 QUnit.test( '.getQueryString()', 2, function ( assert ) {
235 var uri = new mw.Uri( 'http://search.example.com/?q=uri' );
236
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 );
258
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 );
265
266 } );
267
268 QUnit.test( '.clone()', 6, function ( assert ) {
269 var original, clone;
270
271 original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
272 clone = original.clone();
273
274 assert.deepEqual( clone, original, 'clone has equivalent properties' );
275 assert.equal( original.toString(), clone.toString(), 'toString matches original' );
276
277 assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
278
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' );
282
283 clone.query.three = 3;
284
285 assert.deepEqual(
286 original.query,
287 { one: '1', two: '2' },
288 'Properties is deep cloned (bug 37708)'
289 );
290 } );
291
292 QUnit.test( '.toString() after query manipulation', 8, function ( assert ) {
293 var uri;
294
295 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
296 overrideKeys: true
297 } );
298
299 uri.query.n = [ 'x', 'y', 'z' ];
300
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' );
306
307 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
308 overrideKeys: false
309 } );
310
311 // Change query values
312 uri.query.n = [ 'x', 'y', 'z' ];
313
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' );
319
320 // Remove query values
321 uri.query.m.splice( 0, 1 );
322 delete uri.query.n;
323
324 assert.equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
325
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 } );
330
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 } );
337
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 );
362
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 } );
390
391 QUnit.test( 'Advanced URL', 11, function ( assert ) {
392 var uri, queryString, relativePath;
393
394 uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
395
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 );
419
420 assert.equal( uri.getUserInfo(), 'auth', 'user info' );
421
422 assert.equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
423
424 assert.equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
425
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' );
431
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 } );
437
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@=@' );
440
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 } );
467
468 QUnit.test( 'Handle protocol-relative URLs', 5, function ( assert ) {
469 var UriRel, uri;
470
471 UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
472
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' );
475
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' );
478
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' );
481
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' );
484
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 } );
488
489 QUnit.test( 'bug 35658', 2, function ( assert ) {
490 var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
491
492 testProtocol = 'https://';
493 testServer = 'foo.example.org';
494 testPort = '3004';
495 testPath = '/!1qy';
496
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' );
501
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 ) );