Merge "Add ss_active_users in SiteStats::isSane"
[mediawiki.git] / tests / qunit / suites / resources / mediawiki / mediawiki.Uri.test.js
blob9913f5e64014096e20d90fec88cb3a7950f078e7
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 ] )', 10, 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( 'foo.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( 'foo.com/bar/baz', {
131                         strictMode: false
132                 } );
133                 assert.equal( uri.toString(), 'http://foo.com/bar/baz', 'normalize URI without protocol or // in loose mode' );
134         } );
136         QUnit.test( 'Constructor( Object )', 3, function ( assert ) {
137                 var uri = new mw.Uri( {
138                         protocol: 'http',
139                         host: 'www.foo.local',
140                         path: '/this'
141                 } );
142                 assert.equal( uri.toString(), 'http://www.foo.local/this', 'Basic properties' );
144                 uri = new mw.Uri( {
145                         protocol: 'http',
146                         host: 'www.foo.local',
147                         path: '/this',
148                         query: { hi: 'there' },
149                         fragment: 'blah'
150                 } );
151                 assert.equal( uri.toString(), 'http://www.foo.local/this?hi=there#blah', 'More complex properties' );
153                 assert.throws(
154                         function () {
155                                 return new mw.Uri( {
156                                         protocol: 'http',
157                                         host: 'www.foo.local'
158                                 } );
159                         },
160                         function ( e ) {
161                                 return e.message === 'Bad constructor arguments';
162                         },
163                         'Construction failed when missing required properties'
164                 );
165         } );
167         QUnit.test( 'Constructor( empty )', 4, function ( assert ) {
168                 var testuri, MyUri, uri;
170                 testuri = 'http://example.org/w/index.php';
171                 MyUri = mw.UriRelative( testuri );
173                 uri = new MyUri();
174                 assert.equal( uri.toString(), testuri, 'no arguments' );
176                 uri = new MyUri( undefined );
177                 assert.equal( uri.toString(), testuri, 'undefined' );
179                 uri = new MyUri( null );
180                 assert.equal( uri.toString(), testuri, 'null' );
182                 uri = new MyUri( '' );
183                 assert.equal( uri.toString(), testuri, 'empty string' );
184         } );
186         QUnit.test( 'Properties', 8, function ( assert ) {
187                 var uriBase, uri;
189                 uriBase = new mw.Uri( 'http://en.wiki.local/w/api.php' );
191                 uri = uriBase.clone();
192                 uri.fragment = 'frag';
193                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php#frag', 'add a fragment' );
195                 uri = uriBase.clone();
196                 uri.host = 'fr.wiki.local';
197                 uri.port = '8080';
198                 assert.equal( uri.toString(), 'http://fr.wiki.local:8080/w/api.php', 'change host and port' );
200                 uri = uriBase.clone();
201                 uri.query.foo = 'bar';
202                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'add query arguments' );
204                 delete uri.query.foo;
205                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php', 'delete query arguments' );
207                 uri = uriBase.clone();
208                 uri.query.foo = 'bar';
209                 assert.equal( uri.toString(), 'http://en.wiki.local/w/api.php?foo=bar', 'extend query arguments' );
210                 uri.extend( {
211                         foo: 'quux',
212                         pif: 'paf'
213                 } );
214                 assert.ok( uri.toString().indexOf( 'foo=quux' ) >= 0, 'extend query arguments' );
215                 assert.ok( uri.toString().indexOf( 'foo=bar' ) === -1, 'extend query arguments' );
216                 assert.ok( uri.toString().indexOf( 'pif=paf' ) >= 0, 'extend query arguments' );
217         } );
219         QUnit.test( '.getQueryString()', 2, function ( assert ) {
220                 var uri = new mw.Uri( 'http://www.google.com/?q=uri' );
222                 assert.deepEqual(
223                         {
224                                 protocol: uri.protocol,
225                                 host: uri.host,
226                                 port: uri.port,
227                                 path: uri.path,
228                                 query: uri.query,
229                                 fragment: uri.fragment,
230                                 queryString: uri.getQueryString()
231                         },
232                         {
233                                 protocol: 'http',
234                                 host: 'www.google.com',
235                                 port: undefined,
236                                 path: '/',
237                                 query: { q: 'uri' },
238                                 fragment: undefined,
239                                 queryString: 'q=uri'
240                         },
241                         'basic object properties'
242                 );
244                 uri = new mw.Uri( 'https://example.org/mw/index.php?title=Sandbox/7&other=Sandbox/7&foo' );
245                 assert.equal(
246                         uri.getQueryString(),
247                         'title=Sandbox/7&other=Sandbox%2F7&foo',
248                         'title parameter is escaped the wiki-way'
249                 );
251         } );
253         QUnit.test( '.clone()', 6, function ( assert ) {
254                 var original, clone;
256                 original = new mw.Uri( 'http://foo.example.org/index.php?one=1&two=2' );
257                 clone = original.clone();
259                 assert.deepEqual( clone, original, 'clone has equivalent properties' );
260                 assert.equal( original.toString(), clone.toString(), 'toString matches original' );
262                 assert.notStrictEqual( clone, original, 'clone is a different object when compared by reference' );
264                 clone.host = 'bar.example.org';
265                 assert.notEqual( original.host, clone.host, 'manipulating clone did not effect original' );
266                 assert.notEqual( original.toString(), clone.toString(), 'Stringified url no longer matches original' );
268                 clone.query.three = 3;
270                 assert.deepEqual(
271                         original.query,
272                         { 'one': '1', 'two': '2' },
273                         'Properties is deep cloned (bug 37708)'
274                 );
275         } );
277         QUnit.test( '.toString() after query manipulation', 8, function ( assert ) {
278                 var uri;
280                 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
281                         overrideKeys: true
282                 } );
284                 uri.query.n = [ 'x', 'y', 'z' ];
286                 // Verify parts and total length instead of entire string because order
287                 // of iteration can vary.
288                 assert.ok( uri.toString().indexOf( 'm=bar' ), 'toString preserves other values' );
289                 assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ), 'toString parameter includes all values of an array query parameter' );
290                 assert.equal( uri.toString().length, 'http://www.example.com/dir/?m=bar&n=x&n=y&n=z'.length, 'toString matches expected string' );
292                 uri = new mw.Uri( 'http://www.example.com/dir/?m=foo&m=bar&n=1', {
293                         overrideKeys: false
294                 } );
296                 // Change query values
297                 uri.query.n = [ 'x', 'y', 'z' ];
299                 // Verify parts and total length instead of entire string because order
300                 // of iteration can vary.
301                 assert.ok( uri.toString().indexOf( 'm=foo&m=bar' ) >= 0, 'toString preserves other values' );
302                 assert.ok( uri.toString().indexOf( 'n=x&n=y&n=z' ) >= 0, 'toString parameter includes all values of an array query parameter' );
303                 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' );
305                 // Remove query values
306                 uri.query.m.splice( 0, 1 );
307                 delete uri.query.n;
309                 assert.equal( uri.toString(), 'http://www.example.com/dir/?m=bar', 'deletion properties' );
311                 // Remove more query values, leaving an empty array
312                 uri.query.m.splice( 0, 1 );
313                 assert.equal( uri.toString(), 'http://www.example.com/dir/', 'empty array value is ommitted' );
314         } );
316         QUnit.test( 'Advanced URL', 11, function ( assert ) {
317                 var uri, queryString, relativePath;
319                 uri = new mw.Uri( 'http://auth@www.example.com:81/dir/dir.2/index.htm?q1=0&&test1&test2=value+%28escaped%29#top' );
321                 assert.deepEqual(
322                         {
323                                 protocol: uri.protocol,
324                                 user: uri.user,
325                                 password: uri.password,
326                                 host: uri.host,
327                                 port: uri.port,
328                                 path: uri.path,
329                                 query: uri.query,
330                                 fragment: uri.fragment
331                         },
332                         {
333                                 protocol: 'http',
334                                 user: 'auth',
335                                 password: undefined,
336                                 host: 'www.example.com',
337                                 port: '81',
338                                 path: '/dir/dir.2/index.htm',
339                                 query: { q1: '0', test1: null, test2: 'value (escaped)' },
340                                 fragment: 'top'
341                         },
342                         'basic object properties'
343                 );
345                 assert.equal( uri.getUserInfo(), 'auth', 'user info' );
347                 assert.equal( uri.getAuthority(), 'auth@www.example.com:81', 'authority equal to auth@hostport' );
349                 assert.equal( uri.getHostPort(), 'www.example.com:81', 'hostport equal to host:port' );
351                 queryString = uri.getQueryString();
352                 assert.ok( queryString.indexOf( 'q1=0' ) >= 0, 'query param with numbers' );
353                 assert.ok( queryString.indexOf( 'test1' ) >= 0, 'query param with null value is included' );
354                 assert.ok( queryString.indexOf( 'test1=' ) === -1, 'query param with null value does not generate equals sign' );
355                 assert.ok( queryString.indexOf( 'test2=value+%28escaped%29' ) >= 0, 'query param is url escaped' );
357                 relativePath = uri.getRelativePath();
358                 assert.ok( relativePath.indexOf( uri.path ) >= 0, 'path in relative path' );
359                 assert.ok( relativePath.indexOf( uri.getQueryString() ) >= 0, 'query string in relative path' );
360                 assert.ok( relativePath.indexOf( uri.fragment ) >= 0, 'fragement in relative path' );
361         } );
363         QUnit.test( 'Parse a uri with an @ symbol in the path and query', 1, function ( assert ) {
364                 var uri = new mw.Uri( 'http://www.example.com/test@test?x=@uri&y@=uri&z@=@' );
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                                 queryString: uri.getQueryString()
377                         },
378                         {
379                                 protocol: 'http',
380                                 user: undefined,
381                                 password: undefined,
382                                 host: 'www.example.com',
383                                 port: undefined,
384                                 path: '/test@test',
385                                 query: { x: '@uri', 'y@': 'uri', 'z@': '@' },
386                                 fragment: undefined,
387                                 queryString: 'x=%40uri&y%40=uri&z%40=%40'
388                         },
389                         'basic object properties'
390                 );
391         } );
393         QUnit.test( 'Handle protocol-relative URLs', 5, function ( assert ) {
394                 var UriRel, uri;
396                 UriRel = mw.UriRelative( 'glork://en.wiki.local/foo.php' );
398                 uri = new UriRel( '//en.wiki.local/w/api.php' );
399                 assert.equal( uri.protocol, 'glork', 'create protocol-relative URLs with same protocol as document' );
401                 uri = new UriRel( '/foo.com' );
402                 assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in loose mode' );
404                 uri = new UriRel( 'http:/foo.com' );
405                 assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in loose mode' );
407                 uri = new UriRel( '/foo.com', true );
408                 assert.equal( uri.toString(), 'glork://en.wiki.local/foo.com', 'handle absolute paths by supplying protocol and host from document in strict mode' );
410                 uri = new UriRel( 'http:/foo.com', true );
411                 assert.equal( uri.toString(), 'http://en.wiki.local/foo.com', 'handle absolute paths by supplying host from document in strict mode' );
412         } );
414         QUnit.test( 'bug 35658', 2, function ( assert ) {
415                 var testProtocol, testServer, testPort, testPath, UriClass, uri, href;
417                 testProtocol = 'https://';
418                 testServer = 'foo.example.org';
419                 testPort = '3004';
420                 testPath = '/!1qy';
422                 UriClass = mw.UriRelative( testProtocol + testServer + '/some/path/index.html' );
423                 uri = new UriClass( testPath );
424                 href = uri.toString();
425                 assert.equal( href, testProtocol + testServer + testPath, 'Root-relative URL gets host & protocol supplied' );
427                 UriClass = mw.UriRelative( testProtocol + testServer + ':' + testPort + '/some/path.php' );
428                 uri = new UriClass( testPath );
429                 href = uri.toString();
430                 assert.equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
432         } );
433 }( mediaWiki, jQuery ) );