Merge "user: Allow "CAS update failed" exceptions to be normalised"
[lhc/web/wiklou.git] / tests / qunit / suites / resources / mediawiki / mediawiki.Uri.test.js
1 ( function () {
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 } ) );
12
13 [ true, false ].forEach( function ( strictMode ) {
14 QUnit.test( 'Basic construction and properties (' + ( strictMode ? '' : 'non-' ) + 'strict mode)', 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 } );
20
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 );
39
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 } );
61
62 QUnit.test( 'Constructor( String[, Object ] )', function ( assert ) {
63 var uri;
64
65 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
66 overrideKeys: true
67 } );
68
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' );
72
73 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
74 overrideKeys: false
75 } );
76
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' );
81
82 uri = new mw.Uri( 'ftp://usr:pwd@192.0.2.16/' );
83
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 );
107
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 );
117
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 );
129
130 uri = new mw.Uri( 'example.com/bar/baz', {
131 strictMode: false
132 } );
133 assert.strictEqual( uri.toString(), 'http://example.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
134
135 uri = new mw.Uri( 'http://example.com/index.php?key=key&hasOwnProperty=hasOwnProperty&constructor=constructor&watch=watch' );
136 assert.deepEqual(
137 uri.query,
138 {
139 key: 'key',
140 constructor: 'constructor',
141 hasOwnProperty: 'hasOwnProperty',
142 watch: 'watch'
143 },
144 'Keys in query strings support names of Object prototypes (bug T114344)'
145 );
146 } );
147
148 QUnit.test( 'Constructor( Object )', function ( assert ) {
149 var uri = new mw.Uri( {
150 protocol: 'http',
151 host: 'www.foo.local',
152 path: '/this'
153 } );
154 assert.strictEqual( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
155
156 uri = new mw.Uri( {
157 protocol: 'http',
158 host: 'www.foo.local',
159 path: '/this',
160 query: { hi: 'there' },
161 fragment: 'blah'
162 } );
163 assert.strictEqual( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
164
165 assert.throws(
166 function () {
167 return new mw.Uri( {
168 protocol: 'http',
169 host: 'www.foo.local'
170 } );
171 },
172 function ( e ) {
173 return e.message === 'Bad constructor arguments';
174 },
175 'Construction failed when missing required properties'
176 );
177 } );
178
179 QUnit.test( 'Constructor( empty[, Object ] )', function ( assert ) {
180 var testuri, MyUri, uri;
181
182 testuri = 'http://example.org/w/index.php?a=1&a=2';
183 MyUri = mw.UriRelative( testuri );
184
185 uri = new MyUri();
186 assert.strictEqual( uri.toString(), testuri, 'no arguments' );
187
188 uri = new MyUri( undefined );
189 assert.strictEqual( uri.toString(), testuri, 'undefined' );
190
191 uri = new MyUri( null );
192 assert.strictEqual( uri.toString(), testuri, 'null' );
193
194 uri = new MyUri( '' );
195 assert.strictEqual( uri.toString(), testuri, 'empty string' );
196
197 uri = new MyUri( null, { overrideKeys: true } );
198 assert.deepEqual( uri.query, { a: '2' }, 'null, with options' );
199 } );
200
201 QUnit.test( 'Properties', 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.strictEqual( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
209 uri.fragment = 'café';
210 assert.strictEqual( uri.toString(), 'http://en.wiki.local/w/api.php#caf%C3%A9', 'fragment is url-encoded' );
211
212 uri = uriBase.clone();
213 uri.host = 'fr.wiki.local';
214 uri.port = '8080';
215 assert.strictEqual( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
216
217 uri = uriBase.clone();
218 uri.query.foo = 'bar';
219 assert.strictEqual( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
220
221 delete uri.query.foo;
222 assert.strictEqual( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
223
224 uri = uriBase.clone();
225 uri.query.foo = 'bar';
226 assert.strictEqual( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
227 uri.extend( {
228 foo: 'quux',
229 pif: 'paf'
230 } );
231 assert.strictEqual( uri.toString().indexOf( 'foo=quux' ) !== -1, true, 'extend query arguments' );
232 assert.strictEqual( uri.toString().indexOf( 'foo=bar' ) !== -1, false, 'extend query arguments' );
233 assert.strictEqual( uri.toString().indexOf( 'pif=paf' ) !== -1, true, 'extend query arguments' );
234 } );
235
236 QUnit.test( '.getQueryString()', function ( assert ) {
237 var uri = new mw.Uri( 'http://search.example.com/?q=uri' );
238
239 assert.deepEqual(
240 {
241 protocol: uri.protocol,
242 host: uri.host,
243 port: uri.port,
244 path: uri.path,
245 query: uri.query,
246 fragment: uri.fragment,
247 queryString: uri.getQueryString()
248 },
249 {
250 protocol: 'http',
251 host: 'search.example.com',
252 port: undefined,
253 path: '/',
254 query: { q: 'uri' },
255 fragment: undefined,
256 queryString: 'q=uri'
257 },
258 'basic object properties'
259 );
260
261 uri = new mw.Uri( 'https://example.com/mw/index.php?title=Sandbox/7&other=Sandbox/7&foo' );
262 assert.strictEqual(
263 uri.getQueryString(),
264 'title=Sandbox/7&other=Sandbox%2F7&foo',
265 'title parameter is escaped the wiki-way'
266 );
267
268 } );
269
270 QUnit.test( '.clone()', function ( assert ) {
271 var original, clone;
272
273 original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
274 clone = original.clone();
275
276 assert.deepEqual( clone, original, 'clone has equivalent properties' );
277 assert.strictEqual( original.toString(), clone.toString(), 'toString matches original' );
278
279 assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
280
281 clone.host = 'bar.example.org';
282 assert.notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
283 assert.notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
284
285 clone.query.three = 3;
286
287 assert.deepEqual(
288 original.query,
289 { one: '1', two: '2' },
290 'Properties is deep cloned (T39708)'
291 );
292 } );
293
294 QUnit.test( '.toString() after query manipulation', function ( assert ) {
295 var uri;
296
297 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
298 overrideKeys: true
299 } );
300
301 uri.query.n = [ 'x', 'y', 'z' ];
302
303 // Verify parts and total length instead of entire string because order
304 // of iteration can vary.
305 assert.strictEqual( uri.toString().indexOf( 'm=bar' ) !== -1, true, 'toString preserves other values' );
306 assert.strictEqual( uri.toString().indexOf( 'n=x&n=y&n=z' ) !== -1, true, 'toString parameter includes all values of an array query parameter' );
307 assert.strictEqual( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
308
309 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
310 overrideKeys: false
311 } );
312
313 // Change query values
314 uri.query.n = [ 'x', 'y', 'z' ];
315
316 // Verify parts and total length instead of entire string because order
317 // of iteration can vary.
318 assert.strictEqual( uri.toString().indexOf( 'm=foo&m=bar' ) !== -1, true, 'toString preserves other values' );
319 assert.strictEqual( uri.toString().indexOf( 'n=x&n=y&n=z' ) !== -1, true, 'toString parameter includes all values of an array query parameter' );
320 assert.strictEqual( uri.toString().length, 'http://www.example.com/dir/?m=foo&m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
321
322 // Remove query values
323 uri.query.m.splice( 0, 1 );
324 delete uri.query.n;
325
326 assert.strictEqual( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
327
328 // Remove more query values, leaving an empty array
329 uri.query.m.splice( 0, 1 );
330 assert.strictEqual( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
331 } );
332
333 QUnit.test( 'Variable defaultUri', function ( assert ) {
334 var uri,
335 href = 'http://example.org/w/index.php#here',
336 UriClass = mw.UriRelative( function () {
337 return href;
338 } );
339
340 uri = new UriClass();
341 assert.deepEqual(
342 {
343 protocol: uri.protocol,
344 user: uri.user,
345 password: uri.password,
346 host: uri.host,
347 port: uri.port,
348 path: uri.path,
349 query: uri.query,
350 fragment: uri.fragment
351 },
352 {
353 protocol: 'http',
354 user: undefined,
355 password: undefined,
356 host: 'example.org',
357 port: undefined,
358 path: '/w/index.php',
359 query: {},
360 fragment: 'here'
361 },
362 'basic object properties'
363 );
364
365 // Default URI may change, e.g. via history.replaceState, pushState or location.hash (T74334)
366 href = 'https://example.com/wiki/Foo?v=2';
367 uri = new UriClass();
368 assert.deepEqual(
369 {
370 protocol: uri.protocol,
371 user: uri.user,
372 password: uri.password,
373 host: uri.host,
374 port: uri.port,
375 path: uri.path,
376 query: uri.query,
377 fragment: uri.fragment
378 },
379 {
380 protocol: 'https',
381 user: undefined,
382 password: undefined,
383 host: 'example.com',
384 port: undefined,
385 path: '/wiki/Foo',
386 query: { v: '2' },
387 fragment: undefined
388 },
389 'basic object properties'
390 );
391 } );
392
393 QUnit.test( 'Advanced URL', function ( assert ) {
394 var uri, queryString, relativePath;
395
396 uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#caf%C3%A9' );
397
398 assert.deepEqual(
399 {
400 protocol: uri.protocol,
401 user: uri.user,
402 password: uri.password,
403 host: uri.host,
404 port: uri.port,
405 path: uri.path,
406 query: uri.query,
407 fragment: uri.fragment
408 },
409 {
410 protocol: 'http',
411 user: 'auth',
412 password: undefined,
413 host: 'www.example.com',
414 port: '81',
415 path: '/dir/dir.2/index.htm',
416 query: { q1: '0', test1: null, test2: 'value (escaped)' },
417 fragment: 'café'
418 },
419 'basic object properties'
420 );
421
422 assert.strictEqual( uri.getUserInfo(), 'auth', 'user info' );
423
424 assert.strictEqual( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
425
426 assert.strictEqual( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
427
428 queryString = uri.getQueryString();
429 assert.strictEqual( queryString.indexOf( 'q1=0' ) !== -1, true, 'query param with numbers' );
430 assert.strictEqual( queryString.indexOf( 'test1' ) !== -1, true, 'query param with null value is included' );
431 assert.strictEqual( queryString.indexOf( 'test1=' ) !== -1, false, 'query param with null value does not generate equals sign' );
432 assert.strictEqual( queryString.indexOf( 'test2=value+%28escaped%29' ) !== -1, true, 'query param is url escaped' );
433
434 relativePath = uri.getRelativePath();
435 assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
436 assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
437 assert.ok( relativePath.indexOf( mw.Uri.encode( uri.fragment ) ) >= 0, 'escaped fragment in relative path' );
438 } );
439
440 QUnit.test( 'Parse a uri with an @ symbol in the path and query', function ( assert ) {
441 var uri = new mw.Uri( 'http://www.example.com/test@test?x=@uri&y@=uri&z@=@' );
442
443 assert.deepEqual(
444 {
445 protocol: uri.protocol,
446 user: uri.user,
447 password: uri.password,
448 host: uri.host,
449 port: uri.port,
450 path: uri.path,
451 query: uri.query,
452 fragment: uri.fragment,
453 queryString: uri.getQueryString()
454 },
455 {
456 protocol: 'http',
457 user: undefined,
458 password: undefined,
459 host: 'www.example.com',
460 port: undefined,
461 path: '/test@test',
462 query: { x: '@uri', 'y@': 'uri', 'z@': '@' },
463 fragment: undefined,
464 queryString: 'x=%40uri&y%40=uri&z%40=%40'
465 },
466 'basic object properties'
467 );
468 } );
469
470 QUnit.test( 'Handle protocol-relative URLs', function ( assert ) {
471 var UriRel, uri;
472
473 UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
474
475 uri = new UriRel( '//en.wiki.local/w/api.php' );
476 assert.strictEqual( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
477
478 uri = new UriRel( '/foo.com' );
479 assert.strictEqual( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
480
481 uri = new UriRel( 'http:/foo.com' );
482 assert.strictEqual( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
483
484 uri = new UriRel( '/foo.com', true );
485 assert.strictEqual( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
486
487 uri = new UriRel( 'http:/foo.com', true );
488 assert.strictEqual( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
489 } );
490
491 QUnit.test( 'T37658', function ( assert ) {
492 var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
493
494 testProtocol = 'https://';
495 testServer = 'foo.example.org';
496 testPort = '3004';
497 testPath = '/!1qy';
498
499 UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
500 uri = new UriClass( testPath );
501 href = uri.toString();
502 assert.strictEqual( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
503
504 UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
505 uri = new UriClass( testPath );
506 href = uri.toString();
507 assert.strictEqual( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
508 } );
509 }() );