diff --git a/lib/url.js b/lib/url.js index fdf434c6c3..e6915b42c5 100644 --- a/lib/url.js +++ b/lib/url.js @@ -295,9 +295,14 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { out.pathname = '/'; } + //to support http.request + if (out.pathname || out.search) { + out.path = (out.pathname ? out.pathname : '') + + (out.search ? out.search : ''); + } + // finally, reconstruct the href based on what has been validated. out.href = urlFormat(out); - return out; } @@ -366,11 +371,20 @@ function urlResolveObject(source, relative) { // hash is always overridden, no matter what. source.hash = relative.hash; - if (relative.href === '') return source; + if (relative.href === '') { + source.href = urlFormat(source); + return source; + } // hrefs like //foo/bar always cut to the protocol. if (relative.slashes && !relative.protocol) { relative.protocol = source.protocol; + //urlParse appends trailing / to urls like http://www.example.com + if (slashedProtocol[relative.protocol] && + relative.hostname && !relative.pathname) { + relative.path = relative.pathname = '/'; + } + relative.href = urlFormat(relative); return relative; } @@ -383,14 +397,16 @@ function urlResolveObject(source, relative) { // if it is file:, then the host is dropped, // because that's known to be hostless. // anything else is assumed to be absolute. - - if (!slashedProtocol[relative.protocol]) return relative; - + if (!slashedProtocol[relative.protocol]) { + relative.href = urlFormat(relative); + return relative; + } source.protocol = relative.protocol; if (!relative.host && !hostlessProtocol[relative.protocol]) { var relPath = (relative.pathname || '').split('/'); while (relPath.length && !(relative.host = relPath.shift())); if (!relative.host) relative.host = ''; + if (!relative.hostname) relative.hostname = ''; if (relPath[0] !== '') relPath.unshift(''); if (relPath.length < 2) relPath.unshift(''); relative.pathname = relPath.join('/'); @@ -399,9 +415,16 @@ function urlResolveObject(source, relative) { source.search = relative.search; source.query = relative.query; source.host = relative.host || ''; - delete source.auth; - delete source.hostname; + source.auth = relative.auth; + source.hostname = relative.hostname || relative.host; source.port = relative.port; + //to support http.request + if (source.pathname !== undefined || source.search !== undefined) { + source.path = (source.pathname ? source.pathname : '') + + (source.search ? source.search : ''); + } + source.slashes = source.slashes || relative.slashes; + source.href = urlFormat(source); return source; } @@ -416,8 +439,7 @@ function urlResolveObject(source, relative) { srcPath = source.pathname && source.pathname.split('/') || [], relPath = relative.pathname && relative.pathname.split('/') || [], psychotic = source.protocol && - !slashedProtocol[source.protocol] && - source.host !== undefined; + !slashedProtocol[source.protocol]; // if the url is a non-slashed url, then relative // links like ../.. should be able @@ -452,6 +474,8 @@ function urlResolveObject(source, relative) { // it's absolute. source.host = (relative.host || relative.host === '') ? relative.host : source.host; + source.hostname = (relative.hostname || relative.hostname === '') ? + relative.hostname : source.hostname; source.search = relative.search; source.query = relative.query; srcPath = relPath; @@ -469,19 +493,40 @@ function urlResolveObject(source, relative) { // like href='?foo'. // Put this after the other two cases because it simplifies the booleans if (psychotic) { - source.host = srcPath.shift(); + source.hostname = source.host = srcPath.shift(); + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = source.host && source.host.indexOf('@') > 0 ? + source.host.split('@') : false; + if (authInHost) { + source.auth = authInHost.shift(); + source.hostname = authInHost.shift(); + } } source.search = relative.search; source.query = relative.query; + //to support http.request + if (source.pathname !== undefined || source.search !== undefined) { + source.path = (source.pathname ? source.pathname : '') + + (source.search ? source.search : ''); + } + source.href = urlFormat(source); return source; } if (!srcPath.length) { // no path at all. easy. // we've already handled the other stuff above. delete source.pathname; + //to support http.request + if (!source.search) { + source.path = '/' + source.search; + } else { + delete source.path; + } + source.href = urlFormat(source); return source; } - // if a url ENDs in . or .., then it must get a trailing slash. // however, if it ends in anything else non-slashy, // then it must NOT get a trailing slash. @@ -527,7 +572,17 @@ function urlResolveObject(source, relative) { // put the host back if (psychotic) { - source.host = isAbsolute ? '' : srcPath.shift(); + source.hostname = source.host = isAbsolute ? '' : + srcPath.length ? srcPath.shift() : ''; + //occationaly the auth can get stuck only in host + //this especialy happens in cases like + //url.resolveObject('mailto:local1@domain1', 'local2@domain2') + var authInHost = source.host && source.host.indexOf('@') > 0 ? + source.host.split('@') : false; + if (authInHost) { + relative.auth = authInHost.shift(); + source.hostname = authInHost.shift(); + } } mustEndAbs = mustEndAbs || (source.host && srcPath.length); @@ -537,8 +592,14 @@ function urlResolveObject(source, relative) { } source.pathname = srcPath.join('/'); - - + //to support request.http + if (source.pathname !== undefined || source.search !== undefined) { + source.path = (source.pathname ? source.pathname : '') + + (source.search ? source.search : ''); + } + source.auth = relative.auth; + source.slashes = source.slashes || relative.slashes; + source.href = urlFormat(source); return source; } diff --git a/test/simple/test-url.js b/test/simple/test-url.js index ff55eb2016..a70127e102 100644 --- a/test/simple/test-url.js +++ b/test/simple/test-url.js @@ -30,183 +30,232 @@ var url = require('url'), var parseTests = { '//some_path' : { 'href': '//some_path', - 'pathname': '//some_path' + 'pathname': '//some_path', + 'path': '//some_path' }, 'HTTP://www.example.com/' : { 'href': 'http://www.example.com/', 'protocol': 'http:', + 'slashes': true, 'host': 'www.example.com', 'hostname': 'www.example.com', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://www.ExAmPlE.com/' : { 'href': 'http://www.example.com/', 'protocol': 'http:', + 'slashes': true, 'host': 'www.example.com', 'hostname': 'www.example.com', - 'pathname': '/' - + 'pathname': '/', + 'path': '/' }, 'http://user:pw@www.ExAmPlE.com/' : { 'href': 'http://user:pw@www.example.com/', 'protocol': 'http:', + 'slashes': true, 'auth': 'user:pw', 'host': 'user:pw@www.example.com', 'hostname': 'www.example.com', - 'pathname': '/' - + 'pathname': '/', + 'path': '/' }, 'http://USER:PW@www.ExAmPlE.com/' : { 'href': 'http://USER:PW@www.example.com/', 'protocol': 'http:', + 'slashes': true, 'auth': 'USER:PW', 'host': 'USER:PW@www.example.com', 'hostname': 'www.example.com', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://x.com/path?that\'s#all, folks' : { 'href': 'http://x.com/path?that%27s#all,', 'protocol': 'http:', + 'slashes': true, 'host': 'x.com', 'hostname': 'x.com', 'search': '?that%27s', 'query': 'that%27s', 'pathname': '/path', - 'hash': '#all,' + 'hash': '#all,', + 'path': '/path?that%27s' }, 'HTTP://X.COM/Y' : { 'href': 'http://x.com/Y', 'protocol': 'http:', + 'slashes': true, 'host': 'x.com', 'hostname': 'x.com', - 'pathname': '/Y' + 'pathname': '/Y', + 'path': '/Y' }, // an unexpected invalid char in the hostname. 'HtTp://x.y.cOm*a/b/c?d=e#f gi' : { 'href': 'http://x.y.com/*a/b/c?d=e#f', 'protocol': 'http:', + 'slashes': true, 'host': 'x.y.com', 'hostname': 'x.y.com', 'pathname': '/*a/b/c', 'search': '?d=e', 'query': 'd=e', - 'hash': '#f' + 'hash': '#f', + 'path': '/*a/b/c?d=e' }, // make sure that we don't accidentally lcast the path parts. 'HtTp://x.y.cOm*A/b/c?d=e#f gi' : { 'href': 'http://x.y.com/*A/b/c?d=e#f', 'protocol': 'http:', + 'slashes': true, 'host': 'x.y.com', 'hostname': 'x.y.com', 'pathname': '/*A/b/c', 'search': '?d=e', 'query': 'd=e', - 'hash': '#f' + 'hash': '#f', + 'path': '/*A/b/c?d=e' }, 'http://x...y...#p': { 'href': 'http://x...y.../#p', 'protocol': 'http:', + 'slashes': true, 'host': 'x...y...', 'hostname': 'x...y...', 'hash': '#p', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://x/p/"quoted"': { 'href': 'http://x/p/', 'protocol': 'http:', + 'slashes': true, 'host': 'x', 'hostname': 'x', - 'pathname': '/p/' + 'pathname': '/p/', + 'path': '/p/' }, ' Is a URL!': { 'href': 'http://goo.corn/bread', 'protocol': 'http:', + 'slashes': true, 'host': 'goo.corn', 'hostname': 'goo.corn', - 'pathname': '/bread' + 'pathname': '/bread', + 'path': '/bread' }, 'http://www.narwhaljs.org/blog/categories?id=news' : { 'href': 'http://www.narwhaljs.org/blog/categories?id=news', 'protocol': 'http:', + 'slashes': true, 'host': 'www.narwhaljs.org', 'hostname': 'www.narwhaljs.org', 'search': '?id=news', 'query': 'id=news', - 'pathname': '/blog/categories' + 'pathname': '/blog/categories', + 'path': '/blog/categories?id=news' }, 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' : { 'href': 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', 'protocol': 'http:', + 'slashes': true, 'host': 'mt0.google.com', 'hostname': 'mt0.google.com', - 'pathname': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' + 'pathname': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + 'path': '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' }, 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' : { 'href': 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' + '&x=2&y=2&z=3&s=', 'protocol': 'http:', + 'slashes': true, 'host': 'mt0.google.com', 'hostname': 'mt0.google.com', 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=', 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=', - 'pathname': '/vt/lyrs=m@114' + 'pathname': '/vt/lyrs=m@114', + 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' }, 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { 'href': 'http://user:pass@mt0.google.com/vt/lyrs=m@114???' + '&hl=en&src=api&x=2&y=2&z=3&s=', 'protocol': 'http:', + 'slashes': true, 'host': 'user:pass@mt0.google.com', 'auth': 'user:pass', 'hostname': 'mt0.google.com', 'search': '???&hl=en&src=api&x=2&y=2&z=3&s=', 'query': '??&hl=en&src=api&x=2&y=2&z=3&s=', - 'pathname': '/vt/lyrs=m@114' + 'pathname': '/vt/lyrs=m@114', + 'path': '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' }, 'file:///etc/passwd' : { 'href': 'file:///etc/passwd', + 'slashes': true, 'protocol': 'file:', 'pathname': '/etc/passwd', - 'hostname': '' + 'hostname': '', + 'host': '', + 'path': '/etc/passwd' }, 'file://localhost/etc/passwd' : { 'href': 'file://localhost/etc/passwd', 'protocol': 'file:', + 'slashes': true, 'pathname': '/etc/passwd', - 'hostname': 'localhost' + 'hostname': 'localhost', + 'host': 'localhost', + 'path': '/etc/passwd' }, 'file://foo/etc/passwd' : { 'href': 'file://foo/etc/passwd', 'protocol': 'file:', + 'slashes': true, 'pathname': '/etc/passwd', - 'hostname': 'foo' + 'hostname': 'foo', + 'host': 'foo', + 'path': '/etc/passwd' }, 'file:///etc/node/' : { 'href': 'file:///etc/node/', + 'slashes': true, 'protocol': 'file:', 'pathname': '/etc/node/', - 'hostname': '' + 'hostname': '', + 'host': '', + 'path': '/etc/node/' }, 'file://localhost/etc/node/' : { 'href': 'file://localhost/etc/node/', 'protocol': 'file:', + 'slashes': true, 'pathname': '/etc/node/', - 'hostname': 'localhost' + 'hostname': 'localhost', + 'host': 'localhost', + 'path': '/etc/node/' }, 'file://foo/etc/node/' : { 'href': 'file://foo/etc/node/', 'protocol': 'file:', + 'slashes': true, 'pathname': '/etc/node/', - 'hostname': 'foo' + 'hostname': 'foo', + 'host': 'foo', + 'path': '/etc/node/' }, 'http:/baz/../foo/bar' : { 'href': 'http:/baz/../foo/bar', 'protocol': 'http:', - 'pathname': '/baz/../foo/bar' + 'pathname': '/baz/../foo/bar', + 'path': '/baz/../foo/bar' }, 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag' : { 'href': 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag', 'protocol': 'http:', + 'slashes': true, 'host': 'user:pass@example.com:8000', 'auth': 'user:pass', 'port': '8000', @@ -214,10 +263,12 @@ var parseTests = { 'hash': '#frag', 'search': '?baz=quux', 'query': 'baz=quux', - 'pathname': '/foo/bar' + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' }, '//user:pass@example.com:8000/foo/bar?baz=quux#frag' : { 'href': '//user:pass@example.com:8000/foo/bar?baz=quux#frag', + 'slashes': true, 'host': 'user:pass@example.com:8000', 'auth': 'user:pass', 'port': '8000', @@ -225,14 +276,16 @@ var parseTests = { 'hash': '#frag', 'search': '?baz=quux', 'query': 'baz=quux', - 'pathname': '/foo/bar' + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' }, '/foo/bar?baz=quux#frag' : { 'href': '/foo/bar?baz=quux#frag', 'hash': '#frag', 'search': '?baz=quux', 'query': 'baz=quux', - 'pathname': '/foo/bar' + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' }, 'http:/foo/bar?baz=quux#frag' : { 'href': 'http:/foo/bar?baz=quux#frag', @@ -240,7 +293,8 @@ var parseTests = { 'hash': '#frag', 'search': '?baz=quux', 'query': 'baz=quux', - 'pathname': '/foo/bar' + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' }, 'mailto:foo@bar.com?subject=hello' : { 'href': 'mailto:foo@bar.com?subject=hello', @@ -249,12 +303,14 @@ var parseTests = { 'auth' : 'foo', 'hostname' : 'bar.com', 'search': '?subject=hello', - 'query': 'subject=hello' + 'query': 'subject=hello', + 'path': '?subject=hello' }, 'javascript:alert(\'hello\');' : { 'href': 'javascript:alert(\'hello\');', 'protocol': 'javascript:', - 'pathname': 'alert(\'hello\');' + 'pathname': 'alert(\'hello\');', + 'path': 'alert(\'hello\');' }, 'xmpp:isaacschlueter@jabber.org' : { 'href': 'xmpp:isaacschlueter@jabber.org', @@ -266,6 +322,7 @@ var parseTests = { 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar' : { 'href' : 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', 'protocol' : 'http:', + 'slashes': true, 'host' : 'atpass:foo%40bar@127.0.0.1:8080', 'auth' : 'atpass:foo%40bar', 'hostname' : '127.0.0.1', @@ -273,92 +330,122 @@ var parseTests = { 'pathname': '/path', 'search' : '?search=foo', 'query' : 'search=foo', - 'hash' : '#bar' + 'hash' : '#bar', + 'path': '/path?search=foo' }, // IDNA tests 'http://www.日本語.com/' : { 'href': 'http://www.xn--wgv71a119e.com/', 'protocol': 'http:', + 'slashes': true, 'host': 'www.xn--wgv71a119e.com', 'hostname': 'www.xn--wgv71a119e.com', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://example.Bücher.com/' : { 'href': 'http://example.xn--bcher-kva.com/', 'protocol': 'http:', + 'slashes': true, 'host': 'example.xn--bcher-kva.com', 'hostname': 'example.xn--bcher-kva.com', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://www.Äffchen.com/' : { 'href': 'http://www.xn--ffchen-9ta.com/', 'protocol': 'http:', + 'slashes': true, 'host': 'www.xn--ffchen-9ta.com', 'hostname': 'www.xn--ffchen-9ta.com', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://www.Äffchen.cOm*A/b/c?d=e#f gi' : { 'href': 'http://www.xn--ffchen-9ta.com/*A/b/c?d=e#f', 'protocol': 'http:', + 'slashes': true, 'host': 'www.xn--ffchen-9ta.com', 'hostname': 'www.xn--ffchen-9ta.com', 'pathname': '/*A/b/c', 'search': '?d=e', 'query': 'd=e', - 'hash': '#f' + 'hash': '#f', + 'path': '/*A/b/c?d=e' }, 'http://SÉLIER.COM/' : { 'href': 'http://xn--slier-bsa.com/', 'protocol': 'http:', + 'slashes': true, 'host': 'xn--slier-bsa.com', 'hostname': 'xn--slier-bsa.com', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://ليهمابتكلموشعربي؟.ي؟/' : { 'href': 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', 'protocol': 'http:', + 'slashes': true, 'host': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', 'hostname': 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', - 'pathname': '/' + 'pathname': '/', + 'path': '/' }, 'http://➡.ws/➡' : { 'href': 'http://xn--hgi.ws/➡', 'protocol': 'http:', + 'slashes': true, 'host': 'xn--hgi.ws', 'hostname': 'xn--hgi.ws', - 'pathname': '/➡' + 'pathname': '/➡', + 'path': '/➡' }, 'http://bucket_name.s3.amazonaws.com/image.jpg': { protocol: 'http:', + 'slashes': true, slashes: true, host: 'bucket_name.s3.amazonaws.com', hostname: 'bucket_name.s3.amazonaws.com', pathname: '/image.jpg', - href: 'http://bucket_name.s3.amazonaws.com/image.jpg' + href: 'http://bucket_name.s3.amazonaws.com/image.jpg', + 'path': '/image.jpg' }, 'git+http://github.com/joyent/node.git': { protocol: 'git+http:', slashes: true, host: 'github.com', + hostname: 'github.com', pathname: '/joyent/node.git', + path: '/joyent/node.git', href: 'git+http://github.com/joyent/node.git' + }, + //if local1@domain1 is uses as a relative URL it may + //be parse into auth@hostname, but here there is no + //way to make it work in url.parse, I add the test to be explicit + 'local1@domain1': { + 'pathname': 'local1@domain1', + 'path': 'local1@domain1', + 'href': 'local1@domain1' + }, + //While this may seem counter-intuitive, a browser will parse + // as a path. + 'www.example.com' : { + 'href': 'www.example.com', + 'pathname': 'www.example.com', + 'path': 'www.example.com' } }; for (var u in parseTests) { var actual = url.parse(u), expected = parseTests[u]; - for (var i in expected) { - var e = JSON.stringify(expected[i]), - a = JSON.stringify(actual[i]); - assert.equal(e, a, - 'parse(' + u + ').' + i + ' == ' + e + '\nactual: ' + a); - } + + assert.deepEqual(actual, expected); var expected = parseTests[u].href, actual = url.format(parseTests[u]); - assert.equal(expected, actual, + assert.equal(actual, expected, 'format(' + u + ') == ' + u + '\nactual:' + actual); } @@ -370,7 +457,8 @@ var parseTestsWithQueryString = { 'query': { 'baz': 'quux' }, - 'pathname': '/foo/bar' + 'pathname': '/foo/bar', + 'path': '/foo/bar?baz=quux' }, 'http://example.com' : { 'href': 'http://example.com/', @@ -379,18 +467,16 @@ var parseTestsWithQueryString = { 'host': 'example.com', 'hostname': 'example.com', 'query': {}, - 'pathname': '/' + 'search': '', + 'pathname': '/', + 'path': '/' } }; for (var u in parseTestsWithQueryString) { var actual = url.parse(u, true); var expected = parseTestsWithQueryString[u]; - for (var i in expected) { - var e = JSON.stringify(expected[i]), - a = JSON.stringify(actual[i]); - assert.equal(e, a, - 'parse(' + u + ').' + i + ' == ' + e + '\nactual: ' + a); - } + + assert.deepEqual(actual, expected); } // some extra formatting tests, just to verify @@ -546,7 +632,7 @@ var relativeTests = [ relativeTests.forEach(function(relativeTest) { var a = url.resolve(relativeTest[0], relativeTest[1]), e = relativeTest[2]; - assert.equal(e, a, + assert.equal(a, e, 'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e + '\n actual=' + a); }); @@ -607,7 +693,7 @@ var relativeTests2 = [ ['./g', bases[0], 'http://a/b/c/g'], ['g/', bases[0], 'http://a/b/c/g/'], ['/g', bases[0], 'http://a/g'], - ['//g', bases[0], 'http://g'], + ['//g', bases[0], 'http://g/'], // changed with RFC 2396bis //('?y', bases[0], 'http://a/b/c/d;p?y'], ['?y', bases[0], 'http://a/b/c/d;p?y'], @@ -664,7 +750,7 @@ var relativeTests2 = [ ['./g', bases[1], 'http://a/b/c/g'], ['g/', bases[1], 'http://a/b/c/g/'], ['/g', bases[1], 'http://a/g'], - ['//g', bases[1], 'http://g'], + ['//g', bases[1], 'http://g/'], // changed in RFC 2396bis //('?y', bases[1], 'http://a/b/c/?y'], ['?y', bases[1], 'http://a/b/c/d;p?y'], @@ -724,7 +810,7 @@ var relativeTests2 = [ ['./g', bases[4], 'http:///s//a/b/g'], ['g/', bases[4], 'http:///s//a/b/g/'], ['/g', bases[4], 'http:///g'], // may change to http:///s//a/g - ['//g', bases[4], 'http://g'], // may change to http:///s//g + ['//g', bases[4], 'http://g/'], // may change to http:///s//g ['//g/x', bases[4], 'http://g/x'], // may change to http:///s//g/x ['///g', bases[4], 'http:///g'], ['./', bases[4], 'http:///s//a/b/'], @@ -846,13 +932,85 @@ var relativeTests2 = [ ['mini1.xml', 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/', 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'], - ['../b/c', 'foo:a/y/z', 'foo:a/b/c'] + ['../b/c', 'foo:a/y/z', 'foo:a/b/c'], + + //changeing auth + ['http://diff:auth@www.example.com', + 'http://asdf:qwer@www.example.com', + 'http://diff:auth@www.example.com/'] ]; relativeTests2.forEach(function(relativeTest) { var a = url.resolve(relativeTest[1], relativeTest[0]), e = relativeTest[2]; - assert.equal(e, a, + assert.equal(a, e, 'resolve(' + [relativeTest[1], relativeTest[0]] + ') == ' + e + '\n actual=' + a); }); +//if format and parse are inverse operations then +//resolveObject(parse(x), y) == parse(resolve(x, y)) + +//host and hostname are special, in this case a '' value is important +var emptyIsImportant = {'host': true, 'hostname': ''}; + +//format: [from, path, expected] +relativeTests.forEach(function(relativeTest) { + var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]), + expected = url.parse(relativeTest[2]); + + //because of evaluation order + //resolveObject(parse(x), y) == parse(resolve(x, y)) will differ by + //false-ish values. remove all except host and hostname + for (var i in actual) { + if (actual[i] === undefined || + (!emptyIsImportant.hasOwnProperty(i) && !actual[i])) { + delete actual[i]; + } + } + + assert.deepEqual(actual, expected); + + expected = relativeTest[2]; + actual = url.format(actual); + + assert.equal(actual, expected, + 'format(' + actual + ') == ' + expected + '\nactual:' + actual); +}); + +//format: [to, from, result] +// the test: ['.//g', 'f:/a', 'f://g'] is a fundimental problem +// url.parse('f:/a') does not have a host +// url.resolve('f:/a', './/g') does not have a host becuase you have moved +// down to the g directory. i.e. f: //g, however when this url is parsed +// f:// will indicate that the host is g which is not the case. +// it is unclear to me how to keep this information from being lost +// it may be that a pathname of ////g should colapse to /g but this seems +// to be a lot of work for an edge case. Right now I remove the test +if (relativeTests2[181][0] === './/g' && + relativeTests2[181][1] === 'f:/a' && + relativeTests2[181][2] === 'f://g') { + relativeTests2.splice(181,1); +} +relativeTests2.forEach(function(relativeTest) { + var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]), + expected = url.parse(relativeTest[2]); + + //because of evaluation order + //resolveObject(parse(x), y) == parse(resolve(x, y)) will differ by + //false-ish values. remove all except host and hostname + for (var i in actual) { + if (actual[i] === undefined || + (!emptyIsImportant.hasOwnProperty(i) && !actual[i])) { + delete actual[i]; + } + } + + assert.deepEqual(actual, expected); + + var expected = relativeTest[2], + actual = url.format(actual); + + assert.equal(actual, expected, + 'format(' + relativeTest[1] + ') == ' + expected + + '\nactual:' + actual); +});