diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-10-10 03:47:49 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-10-10 03:47:49 +0200 |
commit | d5194154335d6cb30edca9b648083069faf9778c (patch) | |
tree | bcbefa300067dfd79fc5c770862005129406024d /test/test.js | |
download | wallet-core-d5194154335d6cb30edca9b648083069faf9778c.tar.xz |
Squashed 'thirdparty/URI.js/' content from commit b77c167
git-subtree-dir: thirdparty/URI.js
git-subtree-split: b77c167bc201575956ad409333ff032e504b8044
Diffstat (limited to 'test/test.js')
-rw-r--r-- | test/test.js | 1837 |
1 files changed, 1837 insertions, 0 deletions
diff --git a/test/test.js b/test/test.js new file mode 100644 index 000000000..e1ade0c23 --- /dev/null +++ b/test/test.js @@ -0,0 +1,1837 @@ +(function() { + 'use strict'; + /*global window, document, location, URI, URI_pre_lib, IPv6, IPv6_pre_lib, URITemplate, URITemplate_pre_lib, SecondLevelDomains, SecondLevelDomains_pre_lib, urls, test, ok, equal, strictEqual, deepEqual, raises */ + // FIXME: v2.0.0 renamce non-camelCase properties to uppercase + /*jshint camelcase: false, loopfunc: true */ + + test('loaded', function() { + ok(window.URI); + }); + + module('constructing'); + test('URI()', function() { + var u = URI(); + ok(u instanceof URI, 'instanceof URI'); + equal(u.toString(), window.location && window.location.href || '', 'is location (browser) or empty string (node)'); + }); + test('URI(undefined)', function() { + raises(function() { + URI(undefined); + }, TypeError, 'Failing undefined input'); + }); + test('new URI(string)', function() { + var u = new URI('http://example.org/'); + ok(u instanceof URI, 'instanceof URI'); + ok(u._parts.hostname !== undefined, 'host undefined'); + }); + test('new URI(object)', function() { + var u = new URI({protocol: 'http', hostname: 'example.org'}); + ok(u instanceof URI, 'instanceof URI'); + ok(u._parts.hostname !== undefined, 'host undefined'); + }); + test('new URI(Location)', function () { + var u = new URI(location); + equal(u.href(), String(location.href), 'location object'); + }); + test('new URI(undefined)', function() { + var u = new URI(); + ok(u instanceof URI, 'instanceof URI'); + equal(u.toString(), window.location && window.location.href || '', 'is location (browser) or empty string (node)'); + raises(function() { + new URI(undefined); + }, TypeError, 'Failing undefined input'); + }); + (function() { + var element; + + function testDomAttribute(element, attribute) { + test('new URI(Element ' + element.nodeName + ')', function() { + element[attribute] = 'http://example.org/foobar.html'; + + var u = new URI(element); + equal(u.scheme(), 'http', 'scheme'); + equal(u.host(), 'example.org', 'host'); + equal(u.path(), '/foobar.html', 'path'); + + element[attribute] = 'file:///C:/foo/bar.html'; + u = new URI(element); + equal(u.href(), element[attribute], 'file'); + }); + } + + function testUnsupportedDomAttribute(element, attribute) { + test('new URI(unsupported Element ' + element.nodeName + ')', function() { + element[attribute] = 'http://example.org/foobar.html'; + + var u = new URI(element); + equal(u.scheme(), '', 'scheme'); + equal(u.host(), '', 'host'); + equal(u.path(), '', 'path'); + + element[attribute] = 'file:///C:/foo/bar.html'; + u = new URI(element); + equal(u.href(), '', 'file'); + }); + } + + for (var nodeName in URI.domAttributes) { + if (!Object.prototype.hasOwnProperty.call(URI.domAttributes, nodeName) || nodeName === 'input') { + continue; + } + + element = document.createElement(nodeName); + testDomAttribute(element, URI.domAttributes[nodeName]); + } + + element = document.createElement('input'); + element.type = 'image'; + testDomAttribute(element, 'src'); + + element = document.createElement('input'); + testUnsupportedDomAttribute(element, 'src'); + + element = document.createElement('div'); + testUnsupportedDomAttribute(element, 'src'); + })(); + test('new URI(URI)', function() { + var u = new URI(new URI({protocol: 'http', hostname: 'example.org'})); + ok(u instanceof URI, 'instanceof URI'); + ok(u._parts.hostname !== undefined, 'host undefined'); + }); + test('new URI(new Date())', function() { + raises(function() { + new URI(new Date()); + }, TypeError, 'Failing unknown input'); + }); + test('new URI(undefined)', function() { + raises(function() { + new URI(undefined); + }, TypeError, 'Failing undefined input'); + }); + test('new URI()', function() { + var u = new URI(); + ok(u instanceof URI, 'instanceof URI'); + ok(u._parts.hostname === location.hostname || u._parts.hostname === null && location.hostname === '', + 'hostname == location.hostname'); + }); + test('function URI(string)', function() { + var u = new URI('http://example.org/'); + ok(u instanceof URI, 'instanceof URI'); + ok(u._parts.hostname !== undefined, 'host undefined'); + }); + test('new URI(string, string)', function() { + // see http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor + var u = new URI('../foobar.html', 'http://example.org/hello/world.html'); + equal(u+'', 'http://example.org/foobar.html', 'resolve on construct'); + }); + + module('parsing'); + // [].forEach() no IE, lacking interest in polyfilling this... + for (var i = 0, t; (t = urls[i]); i++) { + (function(t){ + test('parse ' + t.name, function() { + var u = new URI(t.url), + key; + + // test URL built from parts + equal(u + '', t._url || t.url, 'toString'); + + // test parsed parts + for (key in t.parts) { + if (Object.hasOwnProperty.call(t.parts, key)) { + equal(u._parts[key], t.parts[key], 'part: ' + key); + } + } + + // test accessors + for (key in t.accessors) { + if (Object.hasOwnProperty.call(t.accessors, key)) { + equal(u[key](), t.accessors[key], 'accessor: ' + key); + } + } + + // test is() + for (key in t.is) { + if (Object.hasOwnProperty.call(t.is, key)) { + equal(u.is(key), t.is[key], 'is: ' + key); + } + } + }); + })(t); + } + + module('mutating basics'); + test('protocol', function() { + var u = new URI('http://example.org/foo.html'); + u.protocol('ftp'); + equal(u.protocol(), 'ftp', 'ftp protocol'); + equal(u+'', 'ftp://example.org/foo.html', 'ftp url'); + + u.protocol(''); + equal(u.protocol(), '', 'relative protocol'); + equal(u+'', '//example.org/foo.html', 'relative-scheme url'); + + u.protocol('f.t-p+0'); + equal(u.protocol(), 'f.t-p+0', 'character profile'); + + try { + u.protocol('f:t'); + ok(false, 'do not accept invalid protocol'); + } catch(e) {} + + u.protocol(null); + equal(u.protocol(), '', 'missing protocol'); + equal(u+'', '//example.org/foo.html', 'missing-scheme url'); + }); + test('username', function() { + var u = new URI('http://example.org/foo.html'); + u.username('hello'); + equal(u.username(), 'hello', 'changed username hello'); + equal(u.password(), '', 'changed passowrd hello'); + equal(u+'', 'http://hello@example.org/foo.html', 'changed url hello'); + + u.username(''); + equal(u.username(), '', 'changed username ""'); + equal(u.password(), '', 'changed passowrd ""'); + equal(u+'', 'http://example.org/foo.html', 'changed url ""'); + }); + test('password', function() { + var u = new URI('http://hello@example.org/foo.html'); + u.password('world'); + equal(u.username(), 'hello', 'changed username world'); + equal(u.password(), 'world', 'changed passowrd world'); + equal(u+'', 'http://hello:world@example.org/foo.html', 'changed url world'); + + u.password(''); + equal(u.username(), 'hello', 'changed username ""'); + equal(u.password(), '', 'changed passowrd ""'); + equal(u+'', 'http://hello@example.org/foo.html', 'changed url ""'); + + u.username('').password('hahaha'); + equal(u.username(), '', 'changed username - password without username'); + equal(u.password(), 'hahaha', 'changed password - password without username'); + equal(u+'', 'http://:hahaha@example.org/foo.html', 'changed url - password without username'); + }); + test('hostname', function() { + var u = new URI('http://example.org/foo.html'); + u.hostname('abc.foobar.lala'); + equal(u.hostname(), 'abc.foobar.lala', 'hostname changed'); + equal(u+'', 'http://abc.foobar.lala/foo.html', 'hostname changed url'); + + u.hostname(''); + equal(u.hostname(), '', 'hostname removed'); + equal(u+'', 'http:///foo.html', 'hostname removed url'); + + raises(function() { + u.hostname('foo\\bar.com'); + }, TypeError, 'Failing backslash detection in hostname'); + }); + test('port', function() { + var u = new URI('http://example.org/foo.html'); + u.port('80'); + equal(u.port(), '80', 'changing port 80'); + equal(u+'', 'http://example.org:80/foo.html', 'changing url 80'); + + u.port(''); + equal(u.port(), '', 'changing port ""'); + equal(u+'', 'http://example.org/foo.html', 'changing url ""'); + }); + test('path', function() { + var u = new URI('http://example.org/foobar.html?query=string'); + u.pathname('/some/path/file.suffix'); + equal(u.pathname(), '/some/path/file.suffix', 'changing pathname "/some/path/file.suffix"'); + equal(u+'', 'http://example.org/some/path/file.suffix?query=string', 'changing url "/some/path/file.suffix"'); + + u.pathname(''); + equal(u.pathname(), '/', 'changing pathname ""'); + equal(u+'', 'http://example.org/?query=string', 'changing url ""'); + + u.pathname('/~userhome/@mine;is %2F and/'); + equal(u.pathname(), '/~userhome/@mine;is%20%2F%20and/', 'path encoding'); + equal(u.pathname(true), '/~userhome/@mine;is %2F and/', 'path decoded'); + + u = new URI('/a/b/c/').relativeTo('/a/b/c/'); + equal(u.pathname(), '', 'empty relative path'); + equal(u.toString(), '', 'empty relative path to string'); + + u.pathname('/'); + equal(u.pathname(), '/', 'empty absolute path'); + equal(u.toString(), '/', 'empty absolute path to string'); + }); + test('URN paths', function() { + var u = new URI('urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66?foo=bar'); + u.pathname('uuid:de305d54-75b4-431b-adb2-eb6b9e546013'); + equal(u.pathname(), 'uuid:de305d54-75b4-431b-adb2-eb6b9e546013'); + equal(u + '', 'urn:uuid:de305d54-75b4-431b-adb2-eb6b9e546013?foo=bar'); + + u.pathname(''); + equal(u.pathname(), '', 'changing pathname ""'); + equal(u+'', 'urn:?foo=bar', 'changing url ""'); + + u.pathname('music:classical:Béla Bártok%3a Concerto for Orchestra'); + equal(u.pathname(), 'music:classical:B%C3%A9la%20B%C3%A1rtok%3A%20Concerto%20for%20Orchestra', 'path encoding'); + equal(u.pathname(true), 'music:classical:Béla Bártok%3A Concerto for Orchestra', 'path decoded'); + }); + test('query', function() { + var u = new URI('http://example.org/foo.html'); + u.query('foo=bar=foo'); + equal(u.query(), 'foo=bar=foo', 'query: foo=bar=foo'); + equal(u.search(), '?foo=bar=foo', 'query: foo=bar=foo - search'); + + u.query('?bar=foo'); + equal(u.query(), 'bar=foo', 'query: ?bar=foo'); + equal(u.search(), '?bar=foo', 'query: ?bar=foo - search'); + + u.query(''); + equal(u.query(), '', 'query: ""'); + equal(u.search(), '', 'query: "" - search'); + equal(u.toString(), 'http://example.org/foo.html'); + + u.search('foo=bar=foo'); + equal(u.query(), 'foo=bar=foo', 'search: foo=bar=foo'); + equal(u.search(), '?foo=bar=foo', 'search: foo=bar=foo - query'); + + u.search('?bar=foo'); + equal(u.query(), 'bar=foo', 'search: ?bar=foo'); + equal(u.search(), '?bar=foo', 'search: ?bar=foo - query'); + + u.search(''); + equal(u.query(), '', 'search: ""'); + equal(u.search(), '', 'search: "" - query'); + + u.query('?foo'); + equal(u.query(), 'foo', 'search: ""'); + equal(u.search(), '?foo', 'search: "" - query'); + + u.search('foo=&foo=bar'); + equal(u.query(), 'foo=&foo=bar', 'search: foo=&foo=bar'); + equal(JSON.stringify(u.query(true)), JSON.stringify({foo: ['', 'bar']}), 'parsed query: {foo:["", "bar"]}'); + + u.search('foo=bar&foo='); + equal(u.query(), 'foo=bar&foo=', 'search: foo=bar&foo='); + equal(JSON.stringify(u.query(true)), JSON.stringify({foo: ['bar', '']}), 'parsed query: {foo:["bar", ""]}'); + + u.search('foo=bar&foo'); + equal(u.query(), 'foo=bar&foo', 'search: foo=bar&foo'); + equal(JSON.stringify(u.query(true)), JSON.stringify({foo: ['bar', null]}), 'parsed query: {foo:["bar", null]}'); + + u.search('foo&foo=bar'); + equal(u.query(), 'foo&foo=bar', 'search: foo&foo=bar'); + equal(JSON.stringify(u.query(true)), JSON.stringify({foo: [null, 'bar']}), 'parsed query: {foo:[null, "bar"]}'); + + // parsing empty query + var t; + t = u.query('?').query(true); + t = u.query('').query(true); + t = u.href('http://example.org').query(true); + }); + test('fragment', function() { + var u = new URI('http://example.org/foo.html'); + u.fragment('foo'); + equal(u.fragment(), 'foo', 'fragment: foo'); + equal(u.hash(), '#foo', 'fragment: foo - hash'); + + u.fragment('#bar'); + equal(u.fragment(), 'bar', 'fragment: #bar'); + equal(u.hash(), '#bar', 'fragment: #bar - hash'); + + u.fragment(''); + equal(u.fragment(), '', 'fragment: ""'); + equal(u.hash(), '', 'fragment: "" - hash'); + equal(u.toString(), 'http://example.org/foo.html'); + + u.hash('foo'); + equal(u.fragment(), 'foo', 'hash: foo'); + equal(u.hash(), '#foo', 'hash: foo - fragment'); + + u.hash('#bar'); + equal(u.fragment(), 'bar', 'hash: #bar'); + equal(u.hash(), '#bar', 'hash: #bar - fragment'); + + u.hash(''); + equal(u.fragment(), '', 'hash: ""'); + equal(u.hash(), '', 'hash: "" - fragment'); + }); + + module('mutating compounds'); + test('host', function() { + var u = new URI('http://foo.bar/foo.html'); + + u.host('example.org:80'); + equal(u.hostname(), 'example.org', 'host changed hostname'); + equal(u.port(), '80', 'host changed port'); + equal(u+'', 'http://example.org:80/foo.html', 'host changed url'); + + u.host('some-domain.com'); + equal(u.hostname(), 'some-domain.com', 'host modified hostname'); + equal(u.port(), '', 'host removed port'); + equal(u+'', 'http://some-domain.com/foo.html', 'host modified url'); + + raises(function() { + u.host('foo\\bar.com'); + }, TypeError, 'Failing backslash detection in host'); + }); + test('origin', function () { + var u = new URI('http://foo.bar/foo.html'); + equal(u.origin(), 'http://foo.bar', 'invalid origin'); + + u.origin('http://bar.foo/bar.html'); + equal(u.origin(), 'http://bar.foo', 'origin didnt change'); + equal(u+'', 'http://bar.foo/foo.html', 'origin path changed'); + }); + test('authority', function() { + var u = new URI('http://foo.bar/foo.html'); + + u.authority('username:password@example.org:80'); + equal(u.username(), 'username', 'authority changed username'); + equal(u.password(), 'password', 'authority changed password'); + equal(u.hostname(), 'example.org', 'authority changed hostname'); + equal(u.port(), '80', 'authority changed port'); + equal(u+'', 'http://username:password@example.org:80/foo.html', 'authority changed url'); + + u.authority('some-domain.com'); + equal(u.username(), '', 'authority removed username'); + equal(u.password(), '', 'authority removed password'); + equal(u.hostname(), 'some-domain.com', 'authority modified hostname'); + equal(u.port(), '', 'authority removed port'); + equal(u+'', 'http://some-domain.com/foo.html', 'authority modified url'); + + raises(function() { + u.authority('username:password@foo\\bar.com:80'); + }, TypeError, 'Failing backslash detection in authority'); + }); + test('userinfo', function() { + var u = new URI('http://foo.bar/foo.html'); + + u.userinfo('username:password'); + equal(u.username(), 'username', 'userinfo changed username-only'); + equal(u.password(), 'password', 'userinfo changed password'); + equal(u+'', 'http://username:password@foo.bar/foo.html', 'userinfo changed url'); + + u.userinfo('walter'); + equal(u.username(), 'walter', 'userinfo removed password'); + equal(u.password(), '', 'userinfo removed password'); + equal(u+'', 'http://walter@foo.bar/foo.html', 'userinfo changed url'); + + u.userinfo(''); + equal(u.username(), '', 'userinfo removed username'); + equal(u.password(), '', 'userinfo removed password'); + equal(u+'', 'http://foo.bar/foo.html', 'userinfo changed url'); + }); + test('href', function() { + var u = new URI('http://foo.bar/foo.html'); + + u.href('ftp://u:p@example.org:123/directory/file.suffix?query=string#fragment'); + equal(u.protocol(), 'ftp', 'href changed protocol'); + equal(u.username(), 'u', 'href changed username'); + equal(u.password(), 'p', 'href changed password'); + equal(u.hostname(), 'example.org', 'href changed hostname'); + equal(u.port(), '123', 'href changed port'); + equal(u.pathname(), '/directory/file.suffix', 'href changed pathname'); + equal(u.search(), '?query=string', 'href changed search'); + equal(u.hash(), '#fragment', 'href changed hash'); + equal(u.href(), 'ftp://u:p@example.org:123/directory/file.suffix?query=string#fragment', 'href removed url'); + + u.href('../path/index.html'); + equal(u.protocol(), '', 'href removed protocol'); + equal(u.username(), '', 'href removed username'); + equal(u.password(), '', 'href removed password'); + equal(u.hostname(), '', 'href removed hostname'); + equal(u.port(), '', 'href removed port'); + equal(u.pathname(), '../path/index.html', 'href removed pathname'); + equal(u.search(), '', 'href removed search'); + equal(u.hash(), '', 'href removed hash'); + equal(u.href(), '../path/index.html', 'href removed url'); + + /*jshint -W053 */ + u.href(new String('/narf')); + /*jshint +W053 */ + equal(u.pathname(), '/narf', 'href from String instance'); + }); + test('resource', function() { + var u = new URI('http://foo.bar/foo.html?hello#world'); + + equal(u.resource(), '/foo.html?hello#world', 'get resource'); + + u.resource('/foo.html?hello#world'); + equal(u.href(), 'http://foo.bar/foo.html?hello#world', 'set resource'); + + u.resource('/world.html'); + equal(u.href(), 'http://foo.bar/world.html', 'set resource path'); + equal(u.resource(), '/world.html', 'get resource path'); + + u.resource('?query'); + equal(u.href(), 'http://foo.bar/?query', 'set resource query'); + equal(u.resource(), '/?query', 'get resource query'); + + u.resource('#fragment'); + equal(u.href(), 'http://foo.bar/#fragment', 'set resource fragment'); + equal(u.resource(), '/#fragment', 'get resource fragment'); + + u.resource('?hello#world'); + equal(u.href(), 'http://foo.bar/?hello#world', 'set resource query+fragment'); + equal(u.resource(), '/?hello#world', 'get resource query+fragment'); + + u.resource('/mars.txt?planet=123'); + equal(u.href(), 'http://foo.bar/mars.txt?planet=123', 'set resource path+query'); + equal(u.resource(), '/mars.txt?planet=123', 'get resource path+query'); + + u.resource('/neptune.txt#foo'); + equal(u.href(), 'http://foo.bar/neptune.txt#foo', 'set resource path+fragment'); + equal(u.resource(), '/neptune.txt#foo', 'get resource path+fragment'); + }); + + module('mutating fractions'); + test('subdomain', function() { + var u = new URI('http://www.example.org/foo.html'); + u.subdomain('foo.bar'); + equal(u.hostname(), 'foo.bar.example.org', 'changed subdomain foo.bar'); + equal(u+'', 'http://foo.bar.example.org/foo.html', 'changed url foo.bar'); + + u.subdomain(''); + equal(u.hostname(), 'example.org', 'changed subdomain ""'); + equal(u+'', 'http://example.org/foo.html', 'changed url ""'); + + u.subdomain('foo.'); + equal(u.hostname(), 'foo.example.org', 'changed subdomain foo.'); + equal(u+'', 'http://foo.example.org/foo.html', 'changed url foo.'); + + }); + test('domain', function() { + var u = new URI('http://www.example.org/foo.html'); + u.domain('foo.bar'); + equal(u.hostname(), 'www.foo.bar', 'changed hostname foo.bar'); + equal(u+'', 'http://www.foo.bar/foo.html', 'changed url foo.bar'); + + raises(function() { + u.domain(''); + }, TypeError, 'Failing empty input'); + + u.hostname('www.example.co.uk'); + equal(u.domain(), 'example.co.uk', 'domain after changed hostname www.example.co.uk'); + equal(u+'', 'http://www.example.co.uk/foo.html', 'url after changed hostname www.example.co.uk'); + equal(u.domain(true), 'co.uk', 'domain after changed hostname www.example.co.uk (TLD of SLD)'); + + u.domain('example.org'); + equal(u.domain(), 'example.org', 'domain after changed domain example.org'); + equal(u+'', 'http://www.example.org/foo.html', 'url after changed domain example.org'); + + u.domain('example.co.uk'); + equal(u.domain(), 'example.co.uk', 'domain after changed domain example.co.uk'); + equal(u+'', 'http://www.example.co.uk/foo.html', 'url after changed domain example.co.uk'); + + u.href('http://test/'); + equal(u.domain(), 'test', 'domain (dot-less)'); + equal(u.subdomain(), '', 'subdomain (dot-less)'); + + u.subdomain('foo'); + equal(u.href(), 'http://foo.test/', 'subdomain set on (dot-less)'); + }); + test('tld', function() { + var u = new URI('http://www.example.org/foo.html'); + u.tld('mine'); + equal(u.tld(), 'mine', 'tld changed'); + equal(u+'', 'http://www.example.mine/foo.html', 'changed url mine'); + + raises(function() { + u.tld(''); + }, TypeError, 'Failing empty input'); + + raises(function() { + u.tld('foo.bar'); + }, TypeError, 'Failing "foo.bar"'); + + u.tld('co.uk'); + equal(u.tld(), 'co.uk', 'tld changed to sld'); + equal(u+'', 'http://www.example.co.uk/foo.html', 'changed url to sld'); + equal(u.tld(true), 'uk', 'TLD of SLD'); + + u.tld('org'); + equal(u.tld(), 'org', 'sld changed to tld'); + equal(u+'', 'http://www.example.org/foo.html', 'changed url to tld'); + + u.hostname('www.examplet.se'); + equal(u.tld(), 'se', 'se tld'); + + }); + test('sld', function() { + // Lets just test them all.. + // Calling URI.is(), URI.domain(), URI.subdomain() allows us to indirectly + // test SLD.has(), SLD.is() and SLD.get() + var u = new URI('http://www.example.org/foo.html'); + equal(u.is('sld'), false, 'is not sld'); + var list = SecondLevelDomains.list; + var tlds = Object.keys(list); + var iTld = tlds.length; + var tld, slds, sld, iSld; + while ( iTld-- ) { + tld = tlds[iTld]; + slds = list[tld].trim().split(/\s+/); + iSld = slds.length; + while ( iSld-- ) { + sld = slds[iSld].trim() + '.' + tld; + u.hostname('www.example.' + sld); + equal(u.is('sld'), true, 'is sld'); + equal(u.domain(), 'example.' + sld, 'domain is example.' + sld); + equal(u.subdomain(), 'www', 'subdomain is www'); + u.hostname('www.example.' + tld); + equal(u.is('sld'), false, 'is not sld'); + } + } + }); + test('directory', function() { + var u = new URI('http://www.example.org/some/directory/foo.html'); + u.directory('/'); + equal(u.path(), '/foo.html', 'changed path '/''); + equal(u+'', 'http://www.example.org/foo.html', 'changed url '/''); + + u.directory(''); + equal(u.path(), '/foo.html', 'changed path ""'); + equal(u+'', 'http://www.example.org/foo.html', 'changed url ""'); + + u.directory('/bar'); + equal(u.path(), '/bar/foo.html', 'changed path "/bar"'); + equal(u+'', 'http://www.example.org/bar/foo.html', 'changed url "/bar"'); + + u.directory('baz'); + equal(u.path(), '/baz/foo.html', 'changed path "baz"'); + equal(u+'', 'http://www.example.org/baz/foo.html', 'changed url "baz"'); + + // relative paths + u = new URI('../some/directory/foo.html'); + u.directory('../other/'); + equal(u.path(), '../other/foo.html', 'changed path "../other/"'); + equal(u+'', '../other/foo.html', 'changed url "../other/"'); + + u.directory('mine'); + equal(u.path(), 'mine/foo.html', 'changed path "mine"'); + equal(u+'', 'mine/foo.html', 'changed url "mine"'); + + u.directory('/'); + equal(u.path(), '/foo.html', 'changed path "/"'); + equal(u+'', '/foo.html', 'changed url "/"'); + + u.directory(''); + equal(u.path(), 'foo.html', 'changed path ""'); + equal(u+'', 'foo.html', 'changed url ""'); + + u.directory('../blubb'); + equal(u.path(), '../blubb/foo.html', 'changed path "../blubb"'); + equal(u+'', '../blubb/foo.html', 'changed url "../blubb"'); + + // encoding + u.path('/some/directory/foo.html'); + u.directory('/~userhome/@mine;is %2F and/'); + equal(u.path(), '/~userhome/@mine;is%20%2F%20and/foo.html', 'directory encoding'); + equal(u.directory(true), '/~userhome/@mine;is %2F and', 'directory decoded'); + }); + test('filename', function() { + var u = new URI('http://www.example.org/some/directory/foo.html'); + u.filename('hello.world'); + equal(u.path(), '/some/directory/hello.world', 'changed path "hello.world"'); + equal(u+'', 'http://www.example.org/some/directory/hello.world', 'changed url "hello.world"'); + + u.filename('hello'); + equal(u.path(), '/some/directory/hello', 'changed path "hello"'); + equal(u+'', 'http://www.example.org/some/directory/hello', 'changed url "hello"'); + + u.filename(''); + equal(u.path(), '/some/directory/', 'changed path ""'); + equal(u+'', 'http://www.example.org/some/directory/', 'changed url ""'); + + u.filename('world'); + equal(u.path(), '/some/directory/world', 'changed path "world"'); + equal(u+'', 'http://www.example.org/some/directory/world', 'changed url "world"'); + + // encoding + u.path('/some/directory/foo.html'); + u.filename('hällo wörld.html'); + equal(u.path(), '/some/directory/h%C3%A4llo%20w%C3%B6rld.html', 'filename encoding'); + equal(u.filename(true), 'hällo wörld.html', 'filename decoded'); + }); + test('suffix', function() { + var u = new URI('http://www.example.org/some/directory/foo.html'); + u.suffix('xml'); + equal(u.path(), '/some/directory/foo.xml', 'changed path "xml"'); + equal(u+'', 'http://www.example.org/some/directory/foo.xml', 'changed url "xml"'); + + u.suffix(''); + equal(u.path(), '/some/directory/foo', 'changed path ""'); + equal(u+'', 'http://www.example.org/some/directory/foo', 'changed url ""'); + + u.suffix('html'); + equal(u.path(), '/some/directory/foo.html', 'changed path "html"'); + equal(u+'', 'http://www.example.org/some/directory/foo.html', 'changed url "html"'); + + // encoding + u.suffix('cört'); + equal(u.path(), '/some/directory/foo.c%C3%B6rt', 'suffix encoding'); + equal(u.suffix(), 'c%C3%B6rt', 'suffix encoded'); // suffix is expected to be alnum! + equal(u.suffix(true), 'cört', 'suffix decoded'); // suffix is expected to be alnum! + }); + test('segment', function() { + var u = new URI('http://www.example.org/some/directory/foo.html'), + s = u.segment(); + + equal(s.join('||'), 'some||directory||foo.html', 'segment get array'); + + u.segment(['hello', 'world', 'foo.html']); + equal(u.path(), '/hello/world/foo.html', 'segment set array'); + + equal(u.segment(0), 'hello', 'segment get 0'); + equal(u.segment(2), 'foo.html', 'segment get 2'); + equal(u.segment(3), undefined, 'segment get 3'); + + u.segment(0, 'goodbye'); + equal(u.path(), '/goodbye/world/foo.html', 'segment set 0'); + u.segment(2, 'bar.html'); + equal(u.path(), '/goodbye/world/bar.html', 'segment set 2'); + u.segment(3, 'zupp'); + equal(u.path(), '/goodbye/world/bar.html/zupp', 'segment set 3'); + u.segment('zapp'); + equal(u.path(), '/goodbye/world/bar.html/zupp/zapp', 'segment append'); + + u.segment(3, ''); + equal(u.path(), '/goodbye/world/bar.html/zapp', 'segment del 3 ""'); + u.segment(3, null); + equal(u.path(), '/goodbye/world/bar.html', 'segment del 3 null'); + + u = new URI('http://www.example.org/some/directory/foo.html'); + equal(u.segment(-1), 'foo.html', 'segment get -1'); + u.segment(-1, 'world.html'); + equal(u.path(), '/some/directory/world.html', 'segment set -1'); + + u = new URI('someurn:foo:bar:baz'); + equal(u.segment().join('||'), 'foo||bar||baz', 'segment get array URN'); + u.segment(1, 'mars'); + equal(u.path(), 'foo:mars:baz', 'segment set 1 URN'); + equal(u.toString(), 'someurn:foo:mars:baz', 'segment set 1 URN'); + + u = new URI('/foo/'); + equal(u.segment().join('||'), 'foo||', 'segment get array trailing empty'); + + u.segment('test'); + equal(u.path(), '/foo/test', 'segment append trailing empty'); + + u.segment(''); + equal(u.path(), '/foo/test/', 'segment append empty trailing'); + u.segment(''); + equal(u.path(), '/foo/test/', 'segment append empty trailing unchanged'); + + u.segment(['', '', 'foo', '', '', 'bar', '', '']); + equal(u.path(), '/foo/bar/', 'segment collapsing empty parts'); + + u = new URI('https://google.com'); + u.segment('//font.ttf//'); + equal(u.path(), '/font.ttf', 'segment removes trailing and leading slashes'); + + u.segment(['/hello', '/world/', '//foo.html']); + equal(u.path(), '/hello/world/foo.html', 'segment set array trimming slashes'); + + u.segment(1, '/mars/'); + equal(u.path(), '/hello/mars/foo.html', 'segment set index trimming slashes'); + }); + test('segmentCoded', function() { + var u = new URI('http://www.example.org/some%20thing/directory/foo.html'), + s = u.segmentCoded(); + + equal(s.join('||'), 'some thing||directory||foo.html', 'segmentCoded get array'); + + u.segmentCoded(['hello/world']); + equal(u.path(), '/hello%2Fworld', 'escape in array'); + + u.segmentCoded('hello/world'); + equal(u.path(), '/hello%2Fworld/hello%2Fworld', 'escape appended value'); + + u.segmentCoded(['hello world', 'mars', 'foo.html']); + equal(u.path(), '/hello%20world/mars/foo.html', 'segmentCoded set array'); + + equal(u.segmentCoded(0), 'hello world', 'segmentCoded get 0'); + equal(u.segmentCoded(2), 'foo.html', 'segmentCoded get 2'); + equal(u.segmentCoded(3), undefined, 'segmentCoded get 3'); + + u.segmentCoded('zapp zerapp'); + equal(u.path(), '/hello%20world/mars/foo.html/zapp%20zerapp', 'segmentCoded append'); + + u.segmentCoded(2, ''); + equal(u.path(), '/hello%20world/mars/zapp%20zerapp', 'segmentCoded del 3 ""'); + u.segmentCoded(2, null); + equal(u.path(), '/hello%20world/mars', 'segmentCoded del 3 null'); + + u.segmentCoded(''); + equal(u.path(), '/hello%20world/mars/', 'segmentCoded append empty trailing'); + u.segmentCoded(''); + equal(u.path(), '/hello%20world/mars/', 'segmentCoded append empty trailing unchanged'); + }); + + module('mutating query strings'); + test('mutating object', function() { + var u = new URI('?foo=bar&baz=bam&baz=bau'), + q = u.query(true); + + q.something = ['new', 'and', 'funky']; + u.query(q); + equal(u.query(), 'foo=bar&baz=bam&baz=bau&something=new&something=and&something=funky', 'adding array'); + + q.foo = undefined; + u.query(q); + equal(u.query(), 'baz=bam&baz=bau&something=new&something=and&something=funky', 'removing field'); + + q.baz = undefined; + u.query(q); + equal(u.query(), 'something=new&something=and&something=funky', 'removing array'); + }); + test('query callback', function() { + var u = URI('?foo=bar'); + u.query(function(data) { + data.foo = 'bam'; + }); + equal(u.query(), 'foo=bam', 'augment argument'); + + u.query(function() { + return { + bla: 'blubb' + }; + }); + equal(u.query(), 'bla=blubb', 'overwrite returned value'); + }); + test('setQuery', function() { + var u = URI('?foo=bar'); + u.setQuery('foo', 'bam'); + equal(u.query(), 'foo=bam', 'set name, value'); + + u.setQuery('array', ['one', 'two']); + equal(u.query(), 'foo=bam&array=one&array=two', 'set name, array'); + + u.query('?foo=bar'); + u.setQuery({'obj': 'bam', foo: 'baz'}); + equal(u.query(), 'foo=baz&obj=bam', 'set {name: value}'); + + u.setQuery({'foo': 'foo', bar: ['1', '2']}); + equal(u.query(), 'foo=foo&obj=bam&bar=1&bar=2', 'set {name: array}'); + + u.query('?foo=bar'); + u.setQuery({'bam': null, 'baz': ''}); + equal(u.query(), 'foo=bar&bam&baz=', 'set {name: null}'); + + u.query('?foo=bar'); + u.setQuery('empty'); + equal(u.query(), 'foo=bar&empty', 'set undefined'); + + u.query('?foo=bar'); + u.setQuery('empty', ''); + equal(u.query(), 'foo=bar&empty=', 'set empty string'); + + u.query(''); + u.setQuery('some value', 'must be encoded because of = and ? and #'); + equal(u.query(), 'some+value=must+be+encoded+because+of+%3D+and+%3F+and+%23', 'encoding'); + equal(u.query(true)['some value'], 'must be encoded because of = and ? and #', 'decoding'); + }); + test('addQuery', function() { + var u = URI('?foo=bar'); + u.addQuery('baz', 'bam'); + equal(u.query(), 'foo=bar&baz=bam', 'add name, value'); + + u.addQuery('array', ['one', 'two']); + equal(u.query(), 'foo=bar&baz=bam&array=one&array=two', 'add name, array'); + + u.query('?foo=bar'); + u.addQuery({'obj': 'bam', foo: 'baz'}); + equal(u.query(), 'foo=bar&foo=baz&obj=bam', 'add {name: value}'); + + u.addQuery({'foo': 'bam', bar: ['1', '2']}); + equal(u.query(), 'foo=bar&foo=baz&foo=bam&obj=bam&bar=1&bar=2', 'add {name: array}'); + + u.query('?foo=bar'); + u.addQuery({'bam': null, 'baz': ''}); + equal(u.query(), 'foo=bar&bam&baz=', 'add {name: null}'); + + u.query('?foo=bar'); + u.addQuery('empty'); + equal(u.query(), 'foo=bar&empty', 'add undefined'); + + u.query('?foo=bar'); + u.addQuery('empty', ''); + equal(u.query(), 'foo=bar&empty=', 'add empty string'); + + u.query('?foo'); + u.addQuery('foo', 'bar'); + equal(u.query(), 'foo=bar', 'add to null value'); + + u.query(''); + u.addQuery('some value', 'must be encoded because of = and ? and #'); + equal(u.query(), 'some+value=must+be+encoded+because+of+%3D+and+%3F+and+%23', 'encoding'); + equal(u.query(true)['some value'], 'must be encoded because of = and ? and #', 'decoding'); + }); + test('removeQuery', function() { + var u = new URI('?foo=bar&foo=baz&foo=bam&obj=bam&bar=1&bar=2&bar=3'); + + u.removeQuery('foo', 'bar'); + equal(u.query(), 'foo=baz&foo=bam&obj=bam&bar=1&bar=2&bar=3', 'removing name, value'); + + u.removeQuery('foo'); + equal(u.query(), 'obj=bam&bar=1&bar=2&bar=3', 'removing name'); + + u.removeQuery('bar', ['1', '3']); + equal(u.query(), 'obj=bam&bar=2', 'removing name, array'); + + u.query('?obj=bam&bar=1&bar=2'); + u.removeQuery('bar', ['2']); + equal(u.query(), 'obj=bam&bar=1', 'removing name, singleton array'); + + u.removeQuery('bar', ['1']); + equal(u.query(), 'obj=bam', 'removing the last value via name, singleton array'); + + u.query('?foo=one&foo=two').removeQuery('foo', ['one', 'two']); + equal(u.query(), '', 'removing name, array, finishes empty'); + + u.query('?foo=one,two').removeQuery('foo', ['one', 'two']); + equal(u.query(), 'foo=one%2Ctwo', 'not removing name, array'); + + u.query('?foo=one,two').removeQuery('foo', ['one,two']); + equal(u.query(), '', 'removing name, singleton array with comma in value'); + + u.query('?foo=bar&foo=baz&foo=bam&obj=bam&bar=1&bar=2&bar=3'); + u.removeQuery(['foo', 'bar']); + equal(u.query(), 'obj=bam', 'removing array'); + + u.query('?bar=1&bar=2'); + u.removeQuery({ bar: 1 }); + equal(u.query(), 'bar=2', 'removing non-string value from array'); + + u.removeQuery({ bar: 2 }); + equal(u.query(), '', 'removing a non-string value'); + + u.query('?foo=bar&foo=baz&foo=bam&obj=bam&bar=1&bar=2&bar=3'); + u.removeQuery({foo: 'bar', obj: undefined, bar: ['1', '2']}); + equal(u.query(), 'foo=baz&foo=bam&bar=3', 'removing object'); + + u.query('?foo=bar&foo=baz&foo=bam&obj=bam&bar=1&bar=2&bar=3'); + u.removeQuery(/^bar/); + equal(u.query(), 'foo=bar&foo=baz&foo=bam&obj=bam', 'removing by RegExp'); + + u.query('?foo=bar&foo=baz&foo=bam&obj=bam&bar=bar&bar=baz&bar=bam'); + u.removeQuery('foo', /[rz]$/); + equal(u.query(), 'foo=bam&obj=bam&bar=bar&bar=baz&bar=bam', 'removing by value RegExp'); + }); + test('duplicateQueryParameters', function() { + var u = new URI('?bar=1&bar=1&bar=1'); + + u.normalizeQuery(); + equal(u.toString(), '?bar=1', 'parameters de-duplicated'); + + u = new URI('?bar=1&bar=1&bar=1'); + u.duplicateQueryParameters(true); + ok(u._parts.duplicateQueryParameters, 'duplicateQueryParameters enabled'); + u.normalizeQuery(); + equal(u.toString(), '?bar=1&bar=1&bar=1', 'parameters NOT de-duplicated'); + ok(u._parts.duplicateQueryParameters, 'duplicateQueryParameters still enabled after normalizeQuery()'); + + u.duplicateQueryParameters(false); + u.normalizeQuery(); + equal(u.toString(), '?bar=1', 'parameters de-duplicated again'); + ok(!u._parts.duplicateQueryParameters, 'duplicateQueryParameters still disabled after normalizeQuery()'); + + URI.duplicateQueryParameters = true; + u = new URI('?bar=1&bar=1&bar=1'); + u.normalizeQuery(); + equal(u.toString(), '?bar=1&bar=1&bar=1', 'global configuration'); + + URI.duplicateQueryParameters = false; + + // test cloning + u = new URI('?bar=1&bar=1&bar=1'); + u = u.duplicateQueryParameters(true).clone(); + ok(u._parts.duplicateQueryParameters, 'duplicateQueryParameters still enabled after clone()'); + u.normalizeQuery(); + equal(u.toString(), '?bar=1&bar=1&bar=1', 'parameters NOT de-duplicated'); + + // test adding + u = new URI('?bar=1&bar=1&bar=1'); + u.duplicateQueryParameters(true); + u.addQuery('bar', 1); + equal(u.toString(), '?bar=1&bar=1&bar=1&bar=1', 'parameters NOT de-duplicated after addQuery()'); + }); + test('escapeQuerySpace', function() { + var u = new URI('?bar=foo+bar&bam+baz=foo'); + var data = u.query(true); + + equal(data.bar, 'foo bar', 'value un-spac-escaped'); + equal(data['bam baz'], 'foo', 'name un-spac-escaped'); + + u.escapeQuerySpace(false); + data = u.query(true); + equal(data.bar, 'foo+bar', 'value not un-spac-escaped'); + equal(data['bam+baz'], 'foo', 'name not un-spac-escaped'); + + u.escapeQuerySpace(true); + data = u.query(true); + + equal(data.bar, 'foo bar', 'value un-spac-escaped again'); + equal(data['bam baz'], 'foo', 'name un-spac-escaped again'); + + u.escapeQuerySpace(false); + + u.addQuery('alpha bravo', 'charlie delta'); + equal(u.toString(), '?bar=foo%2Bbar&bam%2Bbaz=foo&alpha%20bravo=charlie%20delta', 'serialized un/escaped space'); + + URI.escapeQuerySpace = false; + u = new URI('?bar=foo+bar&bam+baz=foo'); + data = u.query(true); + equal(data.bar, 'foo+bar', 'value not un-spac-escaped by default'); + equal(data['bam+baz'], 'foo', 'name not un-spac-escaped by default'); + + // reset + URI.escapeQuerySpace = true; + }); + test('hasQuery', function() { + var u = URI('?string=bar&list=one&list=two&number=123&null&empty=&nested[one]=1&nested[two]=2'); + + // exists + equal(u.hasQuery('string'), true, 'simple exists check - passing'); + equal(u.hasQuery('nono'), false, 'simple exists check - failing'); + + // truthy value + equal(u.hasQuery('string', true), true, 'has truthy value check - passing string'); + equal(u.hasQuery('number', true), true, 'has truthy value check - passing number'); + equal(u.hasQuery('list', true), true, 'has truthy value check - passing list'); + equal(u.hasQuery('empty', true), false, 'has truthy value check - failing empty'); + equal(u.hasQuery('null', true), false, 'has truthy value check - failing null'); + + // falsy value + equal(u.hasQuery('string', false), false, 'has falsy value check - failing string'); + equal(u.hasQuery('number', false), false, 'has falsy value check - failing number'); + equal(u.hasQuery('list', false), false, 'has falsy value check - failing list'); + equal(u.hasQuery('empty', false), true, 'has falsy value check - passing empty'); + equal(u.hasQuery('null', false), true, 'has falsy value check - passing null'); + + // match value + equal(u.hasQuery('string', 'bar'), true, 'value check - passing string'); + equal(u.hasQuery('number', 123), true, 'value check - passing number'); + equal(u.hasQuery('number', '123'), true, 'value check - passing number as string'); + equal(u.hasQuery('list', 'one'), false, 'value check - failing list'); + equal(u.hasQuery('empty', ''), true, 'value check - passing empty'); + equal(u.hasQuery('null', ''), false, 'value check - failing null'); + + // matching RegExp + equal(u.hasQuery('string', /ar$/), true, 'RegExp check - passing string'); + equal(u.hasQuery('number', /2/), true, 'RegExp check - passing number'); + equal(u.hasQuery('string', /nono/), false, 'RegExp check - failing string'); + equal(u.hasQuery('number', /999/), false, 'RegExp check - failing number'); + equal(u.hasQuery(/^nested/), true, 'RegExp name check - passing'); + equal(u.hasQuery(/^nested/, 2), true, 'RegExp name and value - passing number'); + equal(u.hasQuery(/^nested/, '2'), true, 'RegExp name and value - passing number as string'); + equal(u.hasQuery(/^nested/, 'nono'), false, 'RegExp name and value - failing string'); + equal(u.hasQuery(/^nested/, /2/), true, 'RegExp name and value - passing RegExp number'); + equal(u.hasQuery(/^nested/, /3/), false, 'RegExp name and value exists check - failing'); + equal(u.hasQuery(/^lis/, ['one']), false, 'RegExp name andarray check - failing incomplete list'); + equal(u.hasQuery(/^lis/, ['one', 'two']), true, 'RegExp name and array check - passing list'); + + // matching array + equal(u.hasQuery('string', ['one']), false, 'array check - failing string'); + equal(u.hasQuery('list', ['one']), false, 'array check - failing incomplete list'); + equal(u.hasQuery('list', ['one', 'two']), true, 'array check - passing list'); + equal(u.hasQuery('list', ['two', 'one']), true, 'array check - passing unsorted list'); + + // matching part of array + equal(u.hasQuery('string', ['one'], true), false, 'in array check - failing string'); + equal(u.hasQuery('list', 'one', true), true, 'in array check - passing value'); + equal(u.hasQuery('list', ['one'], true), true, 'in array check - passing incomplete list'); + equal(u.hasQuery('list', ['one', 'two'], true), true, 'in array check - passing list'); + equal(u.hasQuery('list', ['two', 'one'], true), true, 'in array check - passing unsorted list'); + equal(u.hasQuery('list', /ne$/, true), true, 'in array check - passing RegExp'); + equal(u.hasQuery('list', [/ne$/], true), true, 'in array check - passing RegExp list'); + + // comparison function + equal(u.hasQuery('string', function(value, name, data) { + equal(value, 'bar', 'Function check - param value'); + equal(name, 'string', 'Function check - param name'); + equal(typeof data, 'object', 'Function check - param data'); + return true; + }), true, 'Function check - passing true'); + equal(u.hasQuery('string', function() { + return false; + }), false, 'Function check - passing false'); + }); + + module('normalizing'); + test('normalize', function() { + var u = new URI('http://www.exämple.org:80/food/woo/.././../baz.html?&foo=bar&&baz=bam&&baz=bau&#'); + u.normalize(); + equal(u+'', 'http://www.xn--exmple-cua.org/baz.html?foo=bar&baz=bam&baz=bau', 'fully normalized URL'); + }); + test('normalizeProtocol', function() { + var u = new URI('hTTp://example.org/foobar.html'); + u.normalizeProtocol(); + equal(u+'', 'http://example.org/foobar.html', 'lowercase http'); + }); + test('normalizeHost', function() { + var u; + + if (window.punycode) { + u = new URI('http://exämple.org/foobar.html'); + u.normalizeHostname(); + equal(u+'', 'http://xn--exmple-cua.org/foobar.html', 'converting IDN to punycode'); + } + + if (window.IPv6) { + u = new URI('http://[fe80:0000:0000:0000:0204:61ff:fe9d:f156]/foobar.html'); + u.normalizeHostname(); + equal(u+'', 'http://[fe80::204:61ff:fe9d:f156]/foobar.html', 'best IPv6 representation'); + } + + if (window.IPv6) { + u = new URI('http://[::1]/foobar.html'); + u.normalizeHostname(); + equal(u+'', 'http://[::1]/foobar.html', 'best IPv6 representation'); + } + + u = new URI('http://wWw.eXamplE.Org/foobar.html'); + u.normalizeHostname(); + equal(u+'', 'http://www.example.org/foobar.html', 'lower case hostname'); + }); + test('normalizePort', function() { + var u = new URI('http://example.org:80/foobar.html'); + u.normalizePort(); + equal(u+'', 'http://example.org/foobar.html', 'dropping port 80 for http'); + + u = new URI('ftp://example.org:80/foobar.html'); + u.normalizePort(); + equal(u+'', 'ftp://example.org:80/foobar.html', 'keeping port 80 for ftp'); + + }); + test('normalizePath', function() { + // relative URL + var u = new URI('/food/bar/baz.html'); + + u.normalizePath(); + equal(u.path(), '/food/bar/baz.html', 'absolute path without change'); + + u.path('food/bar/baz.html').normalizePath(); + equal(u.path(), 'food/bar/baz.html', 'relative path without change'); + + u.path('/food/../bar/baz.html').normalizePath(); + equal(u.path(), '/bar/baz.html', 'single parent'); + + u.path('/food/woo/../../bar/baz.html').normalizePath(); + equal(u.path(), '/bar/baz.html', 'double parent'); + + u.path('/food/woo/../bar/../baz.html').normalizePath(); + equal(u.path(), '/food/baz.html', 'split double parent'); + + u.path('/food/woo/.././../baz.html').normalizePath(); + equal(u.path(), '/baz.html', 'cwd-split double parent'); + + u.path('food/woo/../bar/baz.html').normalizePath(); + equal(u.path(), 'food/bar/baz.html', 'relative parent'); + + u.path('./food/woo/../bar/baz.html').normalizePath(); + equal(u.path(), 'food/bar/baz.html', 'dot-relative parent'); + + // absolute URL + u = new URI('http://example.org/foo/bar/baz.html'); + u.normalizePath(); + equal(u.path(), '/foo/bar/baz.html', 'URL: absolute path without change'); + + u.path('foo/bar/baz.html').normalizePath(); + equal(u.path(), '/foo/bar/baz.html', 'URL: relative path without change'); + + u.path('/foo/../bar/baz.html').normalizePath(); + equal(u.path(), '/bar/baz.html', 'URL: single parent'); + + u.path('/foo/woo/../../bar/baz.html').normalizePath(); + equal(u.path(), '/bar/baz.html', 'URL: double parent'); + + u.path('/foo/woo/../bar/../baz.html').normalizePath(); + equal(u.path(), '/foo/baz.html', 'URL: split double parent'); + + u.path('/foo/woo/.././../baz.html').normalizePath(); + equal(u.path(), '/baz.html', 'URL: cwd-split double parent'); + + u.path('foo/woo/../bar/baz.html').normalizePath(); + equal(u.path(), '/foo/bar/baz.html', 'URL: relative parent'); + + u.path('./foo/woo/../bar/baz.html').normalizePath(); + equal(u.path(), '/foo/bar/baz.html', 'URL: dot-relative parent'); + + u.path('/.//').normalizePath(); + equal(u.path(), '/', 'root /.//'); + + // encoding + u._parts.path = '/~userhome/@mine;is %2F and/'; + u.normalize(); + equal(u.pathname(), '/~userhome/@mine;is%20%2F%20and/', 'path encoding'); + + // relative URL + u = URI('/.').normalizePath(); + equal(u.path(), '/', 'root /.'); + + u = URI('/..').normalizePath(); + equal(u.path(), '/', 'root /..'); + + u = URI('/foo/.').normalizePath(); + equal(u.path(), '/foo/', 'root /foo/.'); + + u = URI('/foo/..').normalizePath(); + equal(u.path(), '/', 'root /foo/..'); + + u = URI('/foo/.bar').normalizePath(); + equal(u.path(), '/foo/.bar', 'root /foo/.bar'); + + u = URI('/foo/..bar').normalizePath(); + equal(u.path(), '/foo/..bar', 'root /foo/..bar'); + + // Percent Encoding normalization has to happen before dot segment normalization + u = URI('/foo/%2E%2E').normalizePath(); + equal(u.path(), '/', 'root /foo/%2E%2E'); + + u = URI('/foo/%2E').normalizePath(); + equal(u.path(), '/foo/', 'root /foo/%2E'); + + u = URI('/foo/%2E%2E%2Fbar').normalizePath(); + equal(u.path(), '/foo/..%2Fbar', 'root /foo/%2E%2E%2Fbar'); + + u = URI('../../../../../www/common/js/app/../../../../www_test/common/js/app/views/view-test.html'); + u.normalize(); + equal(u.path(), '../../../../../www_test/common/js/app/views/view-test.html', 'parent relative'); + + u = URI('/../../../../../www/common/js/app/../../../../www_test/common/js/app/views/view-test.html'); + u.normalize(); + equal(u.path(), '/www_test/common/js/app/views/view-test.html', 'parent absolute'); + + // URNs + u = URI('urn:people:authors:poets:Shel Silverstein'); + u.normalize(); + equal(u.path(), 'people:authors:poets:Shel%20Silverstein'); + + u = URI('urn:people:authors:philosophers:Søren Kierkegaard'); + u.normalize(); + equal(u.path(), 'people:authors:philosophers:S%C3%B8ren%20Kierkegaard'); + + // URNs path separator preserved + u = URI('urn:games:cards:Magic%3A the Gathering'); + u.normalize(); + equal(u.path(), 'games:cards:Magic%3A%20the%20Gathering'); + }); + test('normalizeQuery', function() { + var u = new URI('http://example.org/foobar.html?'); + u.normalizeQuery(); + equal(u+'', 'http://example.org/foobar.html', 'dropping empty query sign'); + + u.query('?&foo=bar&&baz=bam&').normalizeQuery(); + equal(u.query(), 'foo=bar&baz=bam', 'bad query resolution'); + + u.query('?&foo=bar&&baz=bam&&baz=bau&').normalizeQuery(); + equal(u.query(), 'foo=bar&baz=bam&baz=bau', 'bad query resolution'); + + u.query('?&foo=bar&foo=bar').normalizeQuery(); + equal(u.query(), 'foo=bar', 'duplicate key=value resolution'); + }); + test('normalizeFragment', function() { + var u = new URI('http://example.org/foobar.html#'); + u.normalizeFragment(); + equal(u+'', 'http://example.org/foobar.html', 'dropping empty fragment sign'); + }); + test('readable', function() { + var u = new URI('http://foo:bar@www.xn--exmple-cua.org/hello%20world/ä.html?foo%5B%5D=b+är#fragment'); + equal(u.readable(), 'http://www.exämple.org/hello world/ä.html?foo[]=b är#fragment', 'readable URL'); + }); + + module('resolving URLs'); + test('absoluteTo', function() { + // this being '../bar/baz.html?foo=bar' + // base being 'http://example.org/foo/other/file.html' + // return being http://example.org/foo/bar/baz.html?foo=bar' + var tests = [{ + name: 'relative resolve', + url: 'relative/path?blubber=1#hash1', + base: 'http://www.example.org/path/to/file?some=query#hash', + result: 'http://www.example.org/path/to/relative/path?blubber=1#hash1' + }, { + name: 'absolute resolve', + url: '/absolute/path?blubber=1#hash1', + base: 'http://www.example.org/path/to/file?some=query#hash', + result: 'http://www.example.org/absolute/path?blubber=1#hash1' + }, { + name: 'relative resolve full URL', + url: 'relative/path?blubber=1#hash3', + base: 'http://user:pass@www.example.org:1234/path/to/file?some=query#hash', + result: 'http://user:pass@www.example.org:1234/path/to/relative/path?blubber=1#hash3' + }, { + name: 'absolute resolve full URL', + url: '/absolute/path?blubber=1#hash3', + base: 'http://user:pass@www.example.org:1234/path/to/file?some=query#hash', + result: 'http://user:pass@www.example.org:1234/absolute/path?blubber=1#hash3' + }, { + name: 'absolute resolve full URL without scheme', + url: '//user:pass@www.example.org:1234/path/to/file?some=query#hash', + base: 'proto://user:pass@www.example.org:1234/path/to/file?some=query#hash', + result: 'proto://user:pass@www.example.org:1234/path/to/file?some=query#hash' + }, { + name: 'path-relative resolve', + url: './relative/path?blubber=1#hash3', + base: 'http://user:pass@www.example.org:1234/path/to/file?some=query#hash', + result: 'http://user:pass@www.example.org:1234/path/to/relative/path?blubber=1#hash3' + }, { + name: 'path-relative parent resolve', + url: '../relative/path?blubber=1#hash3', + base: 'http://user:pass@www.example.org:1234/path/to/file?some=query#hash', + result: 'http://user:pass@www.example.org:1234/path/relative/path?blubber=1#hash3' + }, { + name: 'path-relative path resolve', + url: './relative/path?blubber=1#hash3', + base: '/path/to/file?some=query#hash', + result: '/path/to/relative/path?blubber=1#hash3' + }, { + name: 'path-relative path resolve 2', + url: 'tofile', + base: '/path/to/file', + result: '/path/to/tofile' + }, { + name: 'path-relative path-root resolve', + url: 'tofile', + base: '/file', + result: '/tofile' + }, { + name: 'path-relative parent path resolve', + url: '../relative/path?blubber=1#hash3', + base: '/path/to/file?some=query#hash', + result: '/path/relative/path?blubber=1#hash3' + }, { + name: 'fragment absolute url', + url: '#hash3', + base: '/path/to/file?some=query#hash', + result: '/path/to/file?some=query#hash3' + }, { + name: 'fragment relative url', + url: '#hash3', + base: 'path/to/file', + result: 'path/to/file#hash3' + }, { + name: 'relative path - urljoin', + url: 'the_relative_url', + base: 'rel/path/', + result: 'rel/path/the_relative_url' + }, { + name: 'relative path file - urljoin', + url: 'the_relative_url', + base: 'rel/path/something', + result: 'rel/path/the_relative_url' + }, { + name: 'relative parent path file - urljoin', + url: '../the_relative_url', + base: 'rel/path/', + result: 'rel/the_relative_url' + }, { + name: 'relative root path file - urljoin', + url: '/the_relative_url', + base: 'rel/path/', + result: '/the_relative_url' + }, { + name: 'relative root file - urljoin', + url: '/the_relative_url', + base: 'http://example.com/rel/path/', + result: 'http://example.com/the_relative_url' + }, { + name: 'absolute passthru - urljoin', + url: 'http://github.com//the_relative_url', + base: 'http://example.com/foo/bar', + result: 'http://github.com//the_relative_url' + }, { + name: 'file paths - urljoin', + url: 'anotherFile', + base: 'aFile', + result: 'anotherFile' + } + ]; + + for (var i = 0, t; (t = tests[i]); i++) { + var u = new URI(t.url), + r = u.absoluteTo(t.base); + + equal(r + '', t.result, t.name); + } + }); + test('absoluteTo - RFC3986 reference resolution', function() { + // http://tools.ietf.org/html/rfc3986#section-5.4 + var base = 'http://a/b/c/d;p?q'; + var map = { + // normal + // 'g:h' : 'g:h', // identified as URN + 'g' : 'http://a/b/c/g', + './g' : 'http://a/b/c/g', + 'g/' : 'http://a/b/c/g/', + '/g' : 'http://a/g', + '//g' : 'http://g/', // added trailing / + '?y' : 'http://a/b/c/d;p?y', + 'g?y' : 'http://a/b/c/g?y', + '#s' : 'http://a/b/c/d;p?q#s', + 'g#s' : 'http://a/b/c/g#s', + 'g?y#s' : 'http://a/b/c/g?y#s', + ';x' : 'http://a/b/c/;x', + 'g;x' : 'http://a/b/c/g;x', + 'g;x?y#s' : 'http://a/b/c/g;x?y#s', + '' : 'http://a/b/c/d;p?q', + '.' : 'http://a/b/c/', + './' : 'http://a/b/c/', + '..' : 'http://a/b/', + '../' : 'http://a/b/', + '../g' : 'http://a/b/g', + '../..' : 'http://a/', + '../../' : 'http://a/', + '../../g' : 'http://a/g', + // abnormal + '../../../g' : 'http://a/g', + '../../../../g' : 'http://a/g' + }; + + for (var key in map) { + var u = new URI(key), + r = u.absoluteTo(base); + + equal(r + '', map[key], 'resolution "' + key + '"'); + } + }); + test('relativeTo', function() { + var tests = [{ + name: 'same parent', + url: '/relative/path?blubber=1#hash1', + base: '/relative/file?some=query#hash', + result: 'path?blubber=1#hash1' + }, { + name: 'direct parent', + url: '/relative/path?blubber=1#hash1', + base: '/relative/sub/file?some=query#hash', + result: '../path?blubber=1#hash1' + }, { + name: 'second parent', + url: '/relative/path?blubber=1#hash1', + base: '/relative/sub/sub/file?some=query#hash', + result: '../../path?blubber=1#hash1' + }, { + name: 'third parent', + url: '/relative/path?blubber=1#hash1', + base: '/relative/sub/foo/sub/file?some=query#hash', + result: '../../../path?blubber=1#hash1' + }, { + name: 'parent top level', + url: '/relative/path?blubber=1#hash1', + base: '/path/to/file?some=query#hash', + result: '../../relative/path?blubber=1#hash1' + }, { + name: 'descendant', + url: '/base/path/with/subdir/inner.html', + base: '/base/path/top.html', + result: 'with/subdir/inner.html' + }, { + name: 'same directory', + url: '/path/', + base: '/path/top.html', + result: './' + }, { + name: 'absolute /', + url: 'http://example.org/foo/bar/bat', + base: 'http://example.org/', + result: 'foo/bar/bat' + }, { + name: 'absolute /foo', + url: 'http://example.org/foo/bar/bat', + base: 'http://example.org/foo', + result: 'foo/bar/bat' + }, { + name: 'absolute /foo/', + url: 'http://example.org/foo/bar/bat', + base: 'http://example.org/foo/', + result: 'bar/bat' + }, { + name: 'same scheme', + url: 'http://example.org/foo/bar/bat', + base: 'http://example.com/foo/', + result: '//example.org/foo/bar/bat' + }, { + name: 'different scheme', + url: 'http://example.org/foo/bar', + base: 'https://example.org/foo/', + result: 'http://example.org/foo/bar' + }, { + name: 'base with no scheme or host', + url: 'http://example.org/foo/bar', + base: '/foo/', + result: 'http://example.org/foo/bar' + }, { + name: 'base with no scheme', + url: 'http://example.org/foo/bar', + base: '//example.org/foo/bar', + result: 'http://example.org/foo/bar' + }, { + name: 'denormalized base', + url: '/foo/bar/bat', + base: '/foo/./bar/', + result: 'bat' + }, { + name: 'denormalized url', + url: '/foo//bar/bat', + base: '/foo/bar/', + result: 'bat' + }, { + name: 'credentials', + url: 'http://user:pass@example.org/foo/bar', + base: 'http://example.org/foo/', + result: '//user:pass@example.org/foo/bar' + }, { + name: 'base credentials', + url: 'http://example.org/foo/bar', + base: 'http://user:pass@example.org/foo/bar', + result: '//example.org/foo/bar' + }, { + name: 'same credentials different host', + url: 'http://user:pass@example.org/foo/bar', + base: 'http://user:pass@example.com/foo/bar', + result: '//user:pass@example.org/foo/bar' + }, { + name: 'different port 1', + url: 'http://example.org/foo/bar', + base: 'http://example.org:8080/foo/bar', + result: '//example.org/foo/bar' + }, { + name: 'different port 2', + url: 'http://example.org:8081/foo/bar', + base: 'http://example.org:8080/foo/bar', + result: '//example.org:8081/foo/bar' + }, { + name: 'different port 3', + url: 'http://example.org:8081/foo/bar', + base: 'http://example.org/foo/bar', + result: '//example.org:8081/foo/bar' + }, { + name: 'same path - fragment', + url: 'http://www.example.com:8080/dir/file#abcd', + base: 'http://www.example.com:8080/dir/file', + result: '#abcd' + }, { + name: 'same path - query', + url: 'http://www.example.com:8080/dir/file?abcd=123', + base: 'http://www.example.com:8080/dir/file', + result: '?abcd=123' + }, { + name: 'same path - query and fragment', + url: 'http://www.example.com:8080/dir/file?abcd=123#alpha', + base: 'http://www.example.com:8080/dir/file', + result: '?abcd=123#alpha' + }, { + name: 'already relative', + url: 'foo/bar', + base: '/foo/', + 'throws': true + }, { + name: 'relative base', + url: '/foo/bar', + base: 'foo/', + 'throws': true + } + ]; + + for (var i = 0, t; (t = tests[i]); i++) { + var u = new URI(t.url), + b = new URI(t.base), + caught = false; + var r; + + try { + r = u.relativeTo(b); + } catch (e) { + caught = true; + } + /*jshint sub:true */ + if (t['throws']) { + /*jshint sub:false */ + ok(caught, t.name + ' should throw exception'); + } else { + ok(!caught, t.name + ' should not throw exception'); + equal(r + '', t.result, t.name); + + var a = r.absoluteTo(t.base); + var n = u.clone().normalize(); + equal(a.toString(), n.toString(), t.name + ' reversed'); + } + } + + equal('b/c', + new URI('http://example.org/a/b/c') + .scheme('') + .authority('') + .relativeTo('/a/') + .toString(), + 'bug #103'); + + equal('b/c', + new URI('//example.org/a/b/c') + .authority('') + .relativeTo('/a/') + .toString(), + 'bug #103 (2)'); + }); + + module('static helpers'); + test('withinString', function() { + /*jshint laxbreak: true */ + var source = 'Hello www.example.com,\n' + + 'http://google.com is a search engine, like http://www.bing.com\n' + + 'http://exämple.org/foo.html?baz=la#bumm is an IDN URL,\n' + + 'http://123.123.123.123/foo.html is IPv4 and http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html is IPv6.\n' + + 'links can also be in parens (http://example.org) or quotes »http://example.org«.'; + var expected = 'Hello <a>www.example.com</a>,\n' + + '<a>http://google.com</a> is a search engine, like <a>http://www.bing.com</a>\n' + + '<a>http://exämple.org/foo.html?baz=la#bumm</a> is an IDN URL,\n' + + '<a>http://123.123.123.123/foo.html</a> is IPv4 and <a>http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html</a> is IPv6.\n' + + 'links can also be in parens (<a>http://example.org</a>) or quotes »<a>http://example.org</a>«.'; + /*jshint laxbreak: false */ + var result = URI.withinString(source, function(url) { + return '<a>' + url + '</a>'; + }); + + equal(result, expected, 'in string URI identification'); + }); + test('withinString - ignore', function() { + var decorate = function(url) { + return '<a>' + url + '</a>'; + }; + /*jshint laxbreak: true */ + var source = 'Hello www.example.com,\n' + + 'proto://example.org/foo.html?baz=la#bumm is an URL.\n'; + var expected = 'Hello <a>www.example.com</a>,\n' + + 'proto://example.org/foo.html?baz=la#bumm is an URL.\n'; + /*jshint laxbreak: false */ + var result = URI.withinString(source, decorate, {ignore: /^proto:/i}); + + equal(result, expected, 'filtered in string URI identification'); + }); + test('withinString - ignoreHtml', function() { + var decorate = function(url) { + return '<a>' + url + '</a>'; + }; + /*jshint laxbreak: true */ + var source = 'Hello www.example.com,\n' + + '<a href=http://example.org/foo.html?baz=la#bumm is an URL</a>.\n' + + '<a href="http://example.org/foo.html?baz=la#bumm> is an URL</a>.\n' + + '<a href=\'http://example.org/foo.html?baz=la#bumm\'> is an URL</a>.\n'; + var expected = 'Hello <a>www.example.com</a>,\n' + + '<a href=http://example.org/foo.html?baz=la#bumm is an URL</a>.\n' + + '<a href="http://example.org/foo.html?baz=la#bumm> is an URL</a>.\n' + + '<a href=\'http://example.org/foo.html?baz=la#bumm\'> is an URL</a>.\n'; + /*jshint laxbreak: false */ + var result = URI.withinString(source, decorate, {ignoreHtml: true}); + + equal(result, expected, 'filtered in string URI identification'); + }); + test('withinString - capture only', function() { + /*jshint laxbreak: true */ + var source = 'Hello www.example.com,\n' + + 'http://google.com is a search engine, like http://www.bing.com\n' + + 'http://exämple.org/foo.html?baz=la#bumm is an IDN URL,\n' + + 'http://123.123.123.123/foo.html is IPv4 and http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html is IPv6.\n' + + 'links can also be in parens (http://example.org) or quotes »http://example.org«.'; + var expected = [ + 'www.example.com', + 'http://google.com', + 'http://www.bing.com', + 'http://exämple.org/foo.html?baz=la#bumm', + 'http://123.123.123.123/foo.html', + 'http://fe80:0000:0000:0000:0204:61ff:fe9d:f156/foobar.html', + 'http://example.org', + 'http://example.org' + ]; + + /*jshint laxbreak: false */ + var links = []; + var result = URI.withinString(source, function(url) { + links.push(url); + }); + + deepEqual(links, expected, 'urls extracted'); + equal(result, source, 'source not modified'); + }); + test('noConflict', function() { + var actual_lib = URI; // actual library; after loading, before noConflict() + var unconflicted = URI.noConflict(); + + strictEqual( unconflicted, actual_lib, 'noConflict() returns the URI object' ); + strictEqual( URI, URI_pre_lib, 'noConflict() restores the `URI` variable' ); + + // restore for other tests + window.URI = actual_lib; + }); + test('noConflict(removeAll=true)', function() { + var actual = { + URI: URI, + URITemplate: URITemplate, + IPv6: IPv6, + SecondLevelDomains: SecondLevelDomains + }; + + var unconflicted = URI.noConflict(true); + + deepEqual( unconflicted, actual, 'noConflict(true) returns the { URI, URITemplate, IPv6, SecondLevelDomains } object' ); + strictEqual( URI, URI_pre_lib, 'noConflict(true) restores the `URI` variable' ); + strictEqual( URITemplate, URITemplate_pre_lib, 'noConflict(true) restores the `URITemplate` variable' ); + strictEqual( IPv6, IPv6_pre_lib, 'noConflict(true) restores the `IPv6` variable' ); + strictEqual( SecondLevelDomains, SecondLevelDomains_pre_lib, 'noConflict(true) restores the `SecondLevelDomains` variable' ); + + // restore for other tests + window.URI = actual.URI; + window.URITemplate = actual.URITemplate; + window.IPv6 = actual.IPv6; + window.SecondLevelDomains = actual.SecondLevelDomains; + }); + test('joinPaths', function() { + var result; + + result = URI.joinPaths('/a/b', '/c', 'd', '/e').toString(); + equal(result, '/a/b/c/d/e', 'absolute paths'); + + result = URI.joinPaths('a/b', 'http://example.com/c', new URI('d/'), '/e').toString(); + equal(result, 'a/b/c/d/e', 'relative path'); + + result = URI.joinPaths('/a/').toString(); + equal(result, '/a/', 'single absolute directory'); + + result = URI.joinPaths('/a').toString(); + equal(result, '/a', 'single absolute segment'); + + result = URI.joinPaths('a').toString(); + equal(result, 'a', 'single relative segment'); + + result = URI.joinPaths('').toString(); + equal(result, '', 'empty string'); + + result = URI.joinPaths().toString(); + equal(result, '', 'no argument'); + + result = URI.joinPaths('', 'a', '', '', 'b').toString(); + equal(result, '/a/b', 'leading empty segment'); + + result = URI.joinPaths('a', '', '', 'b', '', '').toString(); + equal(result, 'a/b/', 'trailing empty segment'); + }); + + module('comparing URLs'); + test('equals', function() { + var u = new URI('http://example.org/foo/bar.html?foo=bar&hello=world&hello=mars#fragment'), + e = [ + 'http://example.org/foo/../foo/bar.html?foo=bar&hello=world&hello=mars#fragment', + 'http://exAmple.org/foo/bar.html?foo=bar&hello=world&hello=mars#fragment', + 'http://exAmple.org:80/foo/bar.html?foo=bar&hello=world&hello=mars#fragment', + 'http://example.org/foo/bar.html?foo=bar&hello=mars&hello=world#fragment', + 'http://example.org/foo/bar.html?hello=mars&hello=world&foo=bar&#fragment' + ], + d = [ + 'http://example.org/foo/../bar.html?foo=bar&hello=world&hello=mars#fragment', + 'http://example.org/foo/bar.html?foo=bar&hello=world&hello=mars#frAgment', + 'http://example.org/foo/bar.html?foo=bar&hello=world&hello=mArs#fragment', + 'http://example.org/foo/bar.hTml?foo=bar&hello=world&hello=mars#fragment', + 'http://example.org:8080/foo/bar.html?foo=bar&hello=world&hello=mars#fragment', + 'http://user:pass@example.org/foo/bar.html?foo=bar&hello=world&hello=mars#fragment', + 'ftp://example.org/foo/bar.html?foo=bar&hello=world&hello=mars#fragment', + 'http://example.org/foo/bar.html?foo=bar&hello=world&hello=mars&hello=jupiter#fragment' + ], + i, c; + + for (i = 0; (c = e[i]); i++) { + equal(u.equals(c), true, 'equality ' + i); + } + + for (i = 0; (c = d[i]); i++) { + equal(u.equals(c), false, 'different ' + i); + } + }); + + module('Charset'); + test('iso8859', function() { + var u = new URI('/ä.html'); + u.normalizePath(); + equal(u.path(), '/%C3%A4.html', 'Unicode'); + + URI.iso8859(); + u = new URI('/ä.html'); + u.normalizePath(); + equal(u.path(), '/%E4.html', 'ISO8859'); + u.path('/ö.html'); + equal(u.path(), '/%F6.html', 'ISO8859'); + + URI.unicode(); + u = new URI('/ä.html'); + u.normalizePath(); + equal(u.path(), '/%C3%A4.html', 'Unicode again'); + + u = new URI('/ä.html'); + u.normalizePath(); + equal(u.path(), '/%C3%A4.html', 'convert unicode start'); + u.iso8859(); + equal(u.path(), '/%E4.html', 'convert iso8859'); + u.unicode(); + equal(u.path(), '/%C3%A4.html', 'convert unicode'); + }); + test('bad charset in QueryString', function() { + var uri = new URI('http://www.google.com.hk/search?q=pennytel%20downloads&sa=%20%CB%D1%20%CB%F7%20&forid=1&prog=aff&ie=GB2312&oe=GB2312&safe=active&source=sdo_sb_html&hl=zh-CN'); + var data = uri.query(true); + + equal(data.sa, '%20%CB%D1%20%CB%F7%20', 'undecodable value returned'); + equal(data.forid, '1', 'decodable value returned'); + + uri.normalizeQuery(); + data = uri.query(true); + equal(data.sa, '%20%CB%D1%20%CB%F7%20', 'undecodable value returned'); + equal(data.forid, '1', 'decodable value returned'); + }); + + module('Encoding'); + test('decode malformed URI', function() { + try { + decodeURIComponent('%%20'); + ok(false, 'decodeURIComponent() must throw URIError: URI malformed'); + } catch(e) {} + + try { + URI.decode('%%20'); + ok(false, 'URI.decode() must throw URIError: URI malformed'); + } catch(e) {} + + equal(URI.decodeQuery('%%20'), '%%20', 'malformed URI component returned'); + equal(URI.decodePathSegment('%%20'), '%%20', 'malformed URI component returned'); + equal(URI.decodeUrnPathSegment('%%20'), '%%20', 'malformed URN component returned'); + }); + test('encodeQuery', function() { + var escapeQuerySpace = URI.escapeQuerySpace; + + URI.escapeQuerySpace = true; + equal(URI.encodeQuery(' '), '+'); + equal(URI.encode(' '), '%20'); + + URI.escapeQuerySpace = false; + equal(URI.encodeQuery(' '), '%20'); + equal(URI.encode(' '), '%20'); + + URI.escapeQuerySpace = escapeQuerySpace; + }); + test('decodeQuery', function() { + var escapeQuerySpace = URI.escapeQuerySpace; + + URI.escapeQuerySpace = true; + equal(URI.decodeQuery('+'), ' '); + equal(URI.decodeQuery('%20'), ' '); + equal(URI.decode('%20'), ' '); + equal(URI.decode('+'), '+'); + + URI.escapeQuerySpace = false; + equal(URI.decodeQuery('+'), '+'); + equal(URI.decodeQuery('%20'), ' '); + equal(URI.decode('%20'), ' '); + equal(URI.decode('+'), '+'); + + URI.escapeQuerySpace = escapeQuerySpace; + }); + test('encodeReserved', function() { + equal(URI.encodeReserved('ä:/?#[]@!$&\'()*+,;='), '%C3%A4:/?#[]@!$&\'()*+,;='); + }); + +})(); |