/* * This file is part of the MediaWiki extension MediaViewer. * * MediaViewer is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MediaViewer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MediaViewer. If not, see . */ ( function () { QUnit.module( 'mmv.routing.Router', QUnit.newMwEnvironment() ); QUnit.test( 'Constructor sanity checks', function ( assert ) { var router; router = new mw.mmv.routing.Router(); assert.ok( router, 'Router created successfully' ); } ); QUnit.test( 'isMediaViewerHash()', function ( assert ) { var router = new mw.mmv.routing.Router(); assert.strictEqual( router.isMediaViewerHash( 'mediaviewer/foo' ), true, 'Legacy hash' ); assert.strictEqual( router.isMediaViewerHash( '#mediaviewer/foo' ), true, 'Legacy hash with #' ); assert.strictEqual( router.isMediaViewerHash( 'mediaviewer' ), true, 'Bare legacy hash' ); assert.strictEqual( router.isMediaViewerHash( '#mediaviewer' ), true, 'Bare legacy hash with #' ); assert.strictEqual( router.isMediaViewerHash( '/media/foo' ), true, 'Normal hash' ); assert.strictEqual( router.isMediaViewerHash( '#/media/foo' ), true, 'Normal hash with #' ); assert.strictEqual( router.isMediaViewerHash( '/media' ), true, 'Bare hash' ); assert.strictEqual( router.isMediaViewerHash( '#/media' ), true, 'Bare hash with #' ); assert.strictEqual( router.isMediaViewerHash( 'foo/media' ), false, 'Foreign hash' ); assert.strictEqual( router.isMediaViewerHash( '' ), false, 'Empty hash' ); } ); QUnit.test( 'createHash()/parseHash()', function ( assert ) { var route, parsedRoute, hash, title, router = new mw.mmv.routing.Router(); route = new mw.mmv.routing.MainFileRoute(); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.deepEqual( parsedRoute, route, 'Bare hash' ); title = new mw.Title( 'File:Foo.png' ); route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Normal hash' ); assert.ok( hash.match( /File:Foo.png/ ), 'Simple filenames remain readable' ); title = new mw.Title( 'File:Foo.png' ); route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); assert.notEqual( hash[ 0 ], '#', 'Leading # is not included in the returned hash' ); parsedRoute = router.parseHash( '#' + hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Leading # is accepted when parsing a hash' ); title = new mw.Title( 'File:Foo.png' ); route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Normal hash' ); assert.ok( hash.match( /File:Foo.png/ ), 'Simple filenames remain readable' ); title = new mw.Title( 'File:Foo/bar.png' ); route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Filename with /' ); assert.notOk( hash.match( 'Foo/bar' ), '/ is encoded' ); title = new mw.Title( 'File:Foo bar.png' ); route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Filename with space' ); assert.notOk( hash.match( 'Foo bar' ), 'space is replaced...' ); assert.ok( hash.match( 'Foo_bar' ), '...with underscore' ); title = new mw.Title( 'File:看門狗 (遊戲).jpg' ); route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Unicode filename' ); title = new mw.Title( 'File:%!"$&\'()*,-./:;=?@\\^_`~+.jpg' ); if ( title ) { route = new mw.mmv.routing.ThumbnailRoute( title ); hash = router.createHash( route ); parsedRoute = router.parseHash( hash ); assert.strictEqual( parsedRoute.fileTitle.getPrefixedDb(), title.getPrefixedDb(), 'Special characters' ); } else { // mw.Title depends on $wgLegalTitleChars - do not fail test if it is non-standard assert.ok( true, 'Skipped' ); } } ); QUnit.test( 'createHash() error handling', function ( assert ) { var router = new mw.mmv.routing.Router(); assert.ok( mw.mmv.testHelpers.getException( function () { return new mw.mmv.routing.ThumbnailRoute(); } ), 'Exception thrown then ThumbnailRoute has no title' ); assert.ok( mw.mmv.testHelpers.getException( function () { router.createHash( this.sandbox.createStubInstance( mw.mmv.routing.Route ) ); } ), 'Exception thrown for unknown Route subclass' ); assert.ok( mw.mmv.testHelpers.getException( function () { router.createHash( {} ); } ), 'Exception thrown for non-Route class' ); } ); QUnit.test( 'parseHash() with invalid hashes', function ( assert ) { var router = new mw.mmv.routing.Router(); assert.notOk( router.parseHash( 'foo' ), 'Non-MMV hash is rejected.' ); assert.notOk( router.parseHash( '#foo' ), 'Non-MMV hash is rejected (with #).' ); assert.notOk( router.parseHash( '/media/foo/bar' ), 'Invalid MMV hash is rejected.' ); assert.notOk( router.parseHash( '#/media/foo/bar' ), 'Invalid MMV hash is rejected (with #).' ); } ); QUnit.test( 'parseHash() backwards compatibility', function ( assert ) { var route, router = new mw.mmv.routing.Router(); route = router.parseHash( '#mediaviewer/File:Foo bar.png' ); assert.strictEqual( route.fileTitle.getPrefixedDb(), 'File:Foo_bar.png', 'Old urls (with space) are handled' ); route = router.parseHash( '#mediaviewer/File:Mexican \'Alien\' Piñata.jpg' ); assert.strictEqual( route.fileTitle.getPrefixedDb(), 'File:Mexican_\'Alien\'_Piñata.jpg', 'Old urls (without percent-encoding) are handled' ); } ); QUnit.test( 'createHashedUrl()', function ( assert ) { var url, route = new mw.mmv.routing.MainFileRoute(), router = new mw.mmv.routing.Router(); url = router.createHashedUrl( route, 'http://example.com/' ); assert.strictEqual( url, 'http://example.com/#/media', 'Url generation works' ); url = router.createHashedUrl( route, 'http://example.com/#foo' ); assert.strictEqual( url, 'http://example.com/#/media', 'Urls with fragments are handled' ); } ); QUnit.test( 'parseLocation()', function ( assert ) { var location, route, router = new mw.mmv.routing.Router(); location = { href: 'http://example.com/foo#mediaviewer/File:Foo.png' }; route = router.parseLocation( location ); assert.strictEqual( route.fileTitle.getPrefixedDb(), 'File:Foo.png', 'Reading location works' ); location = { href: 'http://example.com/foo#/media/File:Foo.png' }; route = router.parseLocation( location ); assert.strictEqual( route.fileTitle.getPrefixedDb(), 'File:Foo.png', 'Reading location works' ); location = { href: 'http://example.com/foo' }; route = router.parseLocation( location ); assert.notOk( route, 'Reading location without fragment part works' ); } ); QUnit.test( 'parseLocation() with real location', function ( assert ) { var route, title, hash, router = new mw.mmv.routing.Router(); // mw.Title does not accept % in page names this.sandbox.stub( mw, 'Title', function ( name ) { return { name: name, getMain: function () { return name.replace( /^File:/, '' ); } }; } ); title = new mw.Title( 'File:%40.png' ); hash = router.createHash( new mw.mmv.routing.ThumbnailRoute( title ) ); window.location.hash = hash; route = router.parseLocation( window.location ); assert.strictEqual( route.fileTitle.getMain(), '%40.png', 'Reading location set via location.hash works' ); if ( window.history ) { window.history.pushState( null, null, '#' + hash ); route = router.parseLocation( window.location ); assert.strictEqual( route.fileTitle.getMain(), '%40.png', 'Reading location set via pushState() works' ); } else { assert.ok( true, 'Skipped pushState() test, not supported on this browser' ); } // reset location, might interfere with other tests window.location.hash = '#'; } ); QUnit.test( 'tokenizeHash()', function ( assert ) { var router = new mw.mmv.routing.Router(); router.legacyPrefix = 'legacy'; router.applicationPrefix = 'prefix'; assert.deepEqual( router.tokenizeHash( '#foo/bar' ), [], 'No known prefix' ); assert.deepEqual( router.tokenizeHash( '#prefix' ), [ 'prefix' ], 'Current prefix, with #' ); assert.deepEqual( router.tokenizeHash( 'prefix' ), [ 'prefix' ], 'Current prefix, without #' ); assert.deepEqual( router.tokenizeHash( '#prefix/bar' ), [ 'prefix', 'bar' ], 'Current prefix, with # and element' ); assert.deepEqual( router.tokenizeHash( 'prefix/bar' ), [ 'prefix', 'bar' ], 'Current prefix, with element without #' ); assert.deepEqual( router.tokenizeHash( '#prefix/bar/baz' ), [ 'prefix', 'bar', 'baz' ], 'Current prefix, with # and 2 elements' ); assert.deepEqual( router.tokenizeHash( 'prefix/bar/baz' ), [ 'prefix', 'bar', 'baz' ], 'Current prefix, with 2 elements without #' ); assert.deepEqual( router.tokenizeHash( '#legacy' ), [ 'legacy' ], 'Legacy prefix, with #' ); assert.deepEqual( router.tokenizeHash( 'legacy' ), [ 'legacy' ], 'Legacy prefix, without #' ); assert.deepEqual( router.tokenizeHash( '#legacy/bar' ), [ 'legacy', 'bar' ], 'Legacy prefix, with # and element' ); assert.deepEqual( router.tokenizeHash( 'legacy/bar' ), [ 'legacy', 'bar' ], 'Legacy prefix, with element without #' ); assert.deepEqual( router.tokenizeHash( '#legacy/bar/baz' ), [ 'legacy', 'bar', 'baz' ], 'Legacy prefix, with # and 2 elements' ); assert.deepEqual( router.tokenizeHash( 'legacy/bar/baz' ), [ 'legacy', 'bar', 'baz' ], 'Legacy prefix, with 2 elements without #' ); } ); }() );