/* eslint-disable max-len */ 'use strict'; require('../common'); var assert = require('assert'); var url = require('url'); // URLs to parse, and expected data // { url : parsed } var parseTests = { '//some_path': { href: '//some_path', pathname: '//some_path', path: '//some_path' }, 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': { protocol: 'http:', slashes: true, host: 'evil-phisher', hostname: 'evil-phisher', pathname: '/foo.html', path: '/foo.html', hash: '#h%5Ca%5Cs%5Ch', href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch' }, 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': { protocol: 'http:', slashes: true, host: 'evil-phisher', hostname: 'evil-phisher', pathname: '/foo.html', search: '?json=%22%5C%22foo%5C%22%22', query: 'json=%22%5C%22foo%5C%22%22', path: '/foo.html?json=%22%5C%22foo%5C%22%22', hash: '#h%5Ca%5Cs%5Ch', href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch' }, 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': { protocol: 'http:', slashes: true, host: 'evil-phisher', hostname: 'evil-phisher', pathname: '/foo.html', path: '/foo.html', hash: '#h%5Ca%5Cs%5Ch?blarg', href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg' }, 'http:\\\\evil-phisher\\foo.html': { protocol: 'http:', slashes: true, host: 'evil-phisher', hostname: 'evil-phisher', pathname: '/foo.html', path: '/foo.html', href: 'http://evil-phisher/foo.html' }, 'HTTP://www.example.com/': { href: 'http://www.example.com/', protocol: 'http:', slashes: true, host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'HTTP://www.example.com': { href: 'http://www.example.com/', protocol: 'http:', slashes: true, host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'http://www.ExAmPlE.com/': { href: 'http://www.example.com/', protocol: 'http:', slashes: true, host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'http://user:pw@www.ExAmPlE.com/': { href: 'http://user:pw@www.example.com/', protocol: 'http:', slashes: true, auth: 'user:pw', host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'http://USER:PW@www.ExAmPlE.com/': { href: 'http://USER:PW@www.example.com/', protocol: 'http:', slashes: true, auth: 'USER:PW', host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'http://user@www.example.com/': { href: 'http://user@www.example.com/', protocol: 'http:', slashes: true, auth: 'user', host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'http://user%3Apw@www.example.com/': { href: 'http://user:pw@www.example.com/', protocol: 'http:', slashes: true, auth: 'user:pw', host: 'www.example.com', hostname: 'www.example.com', pathname: '/', path: '/' }, 'http://x.com/path?that\'s#all, folks': { href: 'http://x.com/path?that%27s#all,%20folks', protocol: 'http:', slashes: true, host: 'x.com', hostname: 'x.com', search: '?that%27s', query: 'that%27s', pathname: '/path', hash: '#all,%20folks', 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', path: '/Y' }, // + not an invalid host character // per https://url.spec.whatwg.org/#host-parsing 'http://x.y.com+a/b/c': { href: 'http://x.y.com+a/b/c', protocol: 'http:', slashes: true, host: 'x.y.com+a', hostname: 'x.y.com+a', pathname: '/b/c', path: '/b/c' }, // 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%20g%3Ch%3Ei', protocol: 'http:', slashes: true, host: 'x.y.com', hostname: 'x.y.com', pathname: ';a/b/c', search: '?d=e', query: 'd=e', hash: '#f%20g%3Ch%3Ei', 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%20g%3Ch%3Ei', protocol: 'http:', slashes: true, host: 'x.y.com', hostname: 'x.y.com', pathname: ';A/b/c', search: '?d=e', query: 'd=e', hash: '#f%20g%3Ch%3Ei', 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: '/', path: '/' }, 'http://x/p/"quoted"': { href: 'http://x/p/%22quoted%22', protocol: 'http:', slashes: true, host: 'x', hostname: 'x', pathname: '/p/%22quoted%22', path: '/p/%22quoted%22' }, ' Is a URL!': { href: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', pathname: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', path: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!' }, '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', 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=', 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', 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: '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', 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: '', host: '', path: '/etc/passwd' }, 'file://localhost/etc/passwd': { href: 'file://localhost/etc/passwd', protocol: 'file:', slashes: true, pathname: '/etc/passwd', 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', host: 'foo', path: '/etc/passwd' }, 'file:///etc/node/': { href: 'file:///etc/node/', slashes: true, protocol: 'file:', pathname: '/etc/node/', hostname: '', host: '', path: '/etc/node/' }, 'file://localhost/etc/node/': { href: 'file://localhost/etc/node/', protocol: 'file:', slashes: true, pathname: '/etc/node/', 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', host: 'foo', path: '/etc/node/' }, 'http:/baz/../foo/bar': { href: 'http:/baz/../foo/bar', protocol: 'http:', 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: 'example.com:8000', auth: 'user:pass', port: '8000', hostname: 'example.com', hash: '#frag', search: '?baz=quux', query: 'baz=quux', 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: 'example.com:8000', auth: 'user:pass', port: '8000', hostname: 'example.com', hash: '#frag', search: '?baz=quux', query: 'baz=quux', 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', path: '/foo/bar?baz=quux' }, 'http:/foo/bar?baz=quux#frag': { href: 'http:/foo/bar?baz=quux#frag', protocol: 'http:', hash: '#frag', search: '?baz=quux', query: 'baz=quux', pathname: '/foo/bar', path: '/foo/bar?baz=quux' }, 'mailto:foo@bar.com?subject=hello': { href: 'mailto:foo@bar.com?subject=hello', protocol: 'mailto:', host: 'bar.com', auth: 'foo', hostname: 'bar.com', search: '?subject=hello', query: 'subject=hello', path: '?subject=hello' }, 'javascript:alert(\'hello\');': { href: 'javascript:alert(\'hello\');', protocol: 'javascript:', pathname: 'alert(\'hello\');', path: 'alert(\'hello\');' }, 'xmpp:isaacschlueter@jabber.org': { href: 'xmpp:isaacschlueter@jabber.org', protocol: 'xmpp:', host: 'jabber.org', auth: 'isaacschlueter', hostname: 'jabber.org' }, '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: '127.0.0.1:8080', auth: 'atpass:foo@bar', hostname: '127.0.0.1', port: '8080', pathname: '/path', search: '?search=foo', query: 'search=foo', hash: '#bar', path: '/path?search=foo' }, 'svn+ssh://foo/bar': { href: 'svn+ssh://foo/bar', host: 'foo', hostname: 'foo', protocol: 'svn+ssh:', pathname: '/bar', path: '/bar', slashes: true }, 'dash-test://foo/bar': { href: 'dash-test://foo/bar', host: 'foo', hostname: 'foo', protocol: 'dash-test:', pathname: '/bar', path: '/bar', slashes: true }, 'dash-test:foo/bar': { href: 'dash-test:foo/bar', host: 'foo', hostname: 'foo', protocol: 'dash-test:', pathname: '/bar', path: '/bar' }, 'dot.test://foo/bar': { href: 'dot.test://foo/bar', host: 'foo', hostname: 'foo', protocol: 'dot.test:', pathname: '/bar', path: '/bar', slashes: true }, 'dot.test:foo/bar': { href: 'dot.test:foo/bar', host: 'foo', hostname: 'foo', protocol: 'dot.test:', pathname: '/bar', path: '/bar' }, // 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: '/', 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: '/', 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: '/', 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%20g%3Ch%3Ei', 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%20g%3Ch%3Ei', 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: '/', path: '/' }, 'http://ليهمابتكلموشعربي؟.ي؟/': { href: 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', protocol: 'http:', slashes: true, host: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', hostname: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', pathname: '/', path: '/' }, 'http://➡.ws/➡': { href: 'http://xn--hgi.ws/➡', protocol: 'http:', slashes: true, host: 'xn--hgi.ws', hostname: 'xn--hgi.ws', pathname: '/➡', path: '/➡' }, 'http://bucket_name.s3.amazonaws.com/image.jpg': { protocol: 'http:', 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', 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' }, // ipv6 support '[fe80::1]': { href: '[fe80::1]', pathname: '[fe80::1]', path: '[fe80::1]' }, 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { protocol: 'coap:', slashes: true, host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', hostname: 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', href: 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', pathname: '/', path: '/' }, 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { protocol: 'coap:', slashes: true, host: '[1080:0:0:0:8:800:200c:417a]:61616', port: '61616', hostname: '1080:0:0:0:8:800:200c:417a', href: 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', pathname: '/', path: '/' }, 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { protocol: 'http:', slashes: true, auth: 'user:password', host: '[3ffe:2a00:100:7031::1]:8080', port: '8080', hostname: '3ffe:2a00:100:7031::1', href: 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', pathname: '/', path: '/' }, 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { protocol: 'coap:', slashes: true, auth: 'u:p', host: '[::192.9.5.5]:61616', port: '61616', hostname: '::192.9.5.5', href: 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', search: '?n=Temperature', query: 'n=Temperature', pathname: '/.well-known/r', path: '/.well-known/r?n=Temperature' }, // empty port 'http://example.com:': { protocol: 'http:', slashes: true, host: 'example.com', hostname: 'example.com', href: 'http://example.com/', pathname: '/', path: '/' }, 'http://example.com:/a/b.html': { protocol: 'http:', slashes: true, host: 'example.com', hostname: 'example.com', href: 'http://example.com/a/b.html', pathname: '/a/b.html', path: '/a/b.html' }, 'http://example.com:?a=b': { protocol: 'http:', slashes: true, host: 'example.com', hostname: 'example.com', href: 'http://example.com/?a=b', search: '?a=b', query: 'a=b', pathname: '/', path: '/?a=b' }, 'http://example.com:#abc': { protocol: 'http:', slashes: true, host: 'example.com', hostname: 'example.com', href: 'http://example.com/#abc', hash: '#abc', pathname: '/', path: '/' }, 'http://[fe80::1]:/a/b?a=b#abc': { protocol: 'http:', slashes: true, host: '[fe80::1]', hostname: 'fe80::1', href: 'http://[fe80::1]/a/b?a=b#abc', search: '?a=b', query: 'a=b', hash: '#abc', pathname: '/a/b', path: '/a/b?a=b' }, 'http://-lovemonsterz.tumblr.com/rss': { protocol: 'http:', slashes: true, host: '-lovemonsterz.tumblr.com', hostname: '-lovemonsterz.tumblr.com', href: 'http://-lovemonsterz.tumblr.com/rss', pathname: '/rss', path: '/rss', }, 'http://-lovemonsterz.tumblr.com:80/rss': { protocol: 'http:', slashes: true, port: '80', host: '-lovemonsterz.tumblr.com:80', hostname: '-lovemonsterz.tumblr.com', href: 'http://-lovemonsterz.tumblr.com:80/rss', pathname: '/rss', path: '/rss', }, 'http://user:pass@-lovemonsterz.tumblr.com/rss': { protocol: 'http:', slashes: true, auth: 'user:pass', host: '-lovemonsterz.tumblr.com', hostname: '-lovemonsterz.tumblr.com', href: 'http://user:pass@-lovemonsterz.tumblr.com/rss', pathname: '/rss', path: '/rss', }, 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': { protocol: 'http:', slashes: true, auth: 'user:pass', port: '80', host: '-lovemonsterz.tumblr.com:80', hostname: '-lovemonsterz.tumblr.com', href: 'http://user:pass@-lovemonsterz.tumblr.com:80/rss', pathname: '/rss', path: '/rss', }, 'http://_jabber._tcp.google.com/test': { protocol: 'http:', slashes: true, host: '_jabber._tcp.google.com', hostname: '_jabber._tcp.google.com', href: 'http://_jabber._tcp.google.com/test', pathname: '/test', path: '/test', }, 'http://user:pass@_jabber._tcp.google.com/test': { protocol: 'http:', slashes: true, auth: 'user:pass', host: '_jabber._tcp.google.com', hostname: '_jabber._tcp.google.com', href: 'http://user:pass@_jabber._tcp.google.com/test', pathname: '/test', path: '/test', }, 'http://_jabber._tcp.google.com:80/test': { protocol: 'http:', slashes: true, port: '80', host: '_jabber._tcp.google.com:80', hostname: '_jabber._tcp.google.com', href: 'http://_jabber._tcp.google.com:80/test', pathname: '/test', path: '/test', }, 'http://user:pass@_jabber._tcp.google.com:80/test': { protocol: 'http:', slashes: true, auth: 'user:pass', port: '80', host: '_jabber._tcp.google.com:80', hostname: '_jabber._tcp.google.com', href: 'http://user:pass@_jabber._tcp.google.com:80/test', pathname: '/test', path: '/test', }, 'http://x:1/\' <>"`/{}|\\^~`/': { protocol: 'http:', slashes: true, host: 'x:1', port: '1', hostname: 'x', pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/' }, 'http://a@b@c/': { protocol: 'http:', slashes: true, auth: 'a@b', host: 'c', hostname: 'c', href: 'http://a%40b@c/', path: '/', pathname: '/' }, 'http://a@b?@c': { protocol: 'http:', slashes: true, auth: 'a', host: 'b', hostname: 'b', href: 'http://a@b/?@c', path: '/?@c', pathname: '/', search: '?@c', query: '@c' }, 'http://a\r" \t\n<\'b:b@c\r\nd/e?f':{ protocol: 'http:', slashes: true, auth: 'a\r" \t\n<\'b:b', host: 'c', port: null, hostname: 'c', hash: null, search: '?f', query: 'f', pathname: '%0D%0Ad/e', path: '%0D%0Ad/e?f', href: 'http://a%0D%22%20%09%0A%3C\'b:b@c/%0D%0Ad/e?f' }, // git urls used by npm 'git+ssh://git@github.com:npm/npm': { protocol: 'git+ssh:', slashes: true, auth: 'git', host: 'github.com', port: null, hostname: 'github.com', hash: null, search: null, query: null, pathname: '/:npm/npm', path: '/:npm/npm', href: 'git+ssh://git@github.com/:npm/npm' }, 'https://*': { protocol: 'https:', slashes: true, auth: null, host: '', port: null, hostname: '', hash: null, search: null, query: null, pathname: '/*', path: '/*', href: 'https:///*' } }; for (const u in parseTests) { let actual = url.parse(u); const spaced = url.parse(` \t ${u}\n\t`); let expected = Object.assign(new url.Url(), parseTests[u]); Object.keys(actual).forEach(function(i) { if (expected[i] === undefined && actual[i] === null) { expected[i] = null; } }); assert.deepStrictEqual(actual, expected); assert.deepStrictEqual(spaced, expected); expected = parseTests[u].href; actual = url.format(parseTests[u]); assert.equal(actual, expected, 'format(' + u + ') == ' + u + '\nactual:' + actual); } function createWithNoPrototype(properties = []) { const noProto = Object.create(null); properties.forEach((property) => { noProto[property.key] = property.value; }); return noProto; } function check(actual, expected) { assert.notStrictEqual(Object.getPrototypeOf(actual), Object.prototype); assert.deepStrictEqual(Object.keys(actual).sort(), Object.keys(expected).sort()); Object.keys(expected).forEach(function(key) { assert.deepStrictEqual(actual[key], expected[key]); }); } var parseTestsWithQueryString = { '/foo/bar?baz=quux#frag': { href: '/foo/bar?baz=quux#frag', hash: '#frag', search: '?baz=quux', query: createWithNoPrototype([{key: 'baz', value: 'quux'}]), pathname: '/foo/bar', path: '/foo/bar?baz=quux' }, 'http://example.com': { href: 'http://example.com/', protocol: 'http:', slashes: true, host: 'example.com', hostname: 'example.com', query: createWithNoPrototype(), search: '', pathname: '/', path: '/' }, '/example': { protocol: null, slashes: null, auth: undefined, host: null, port: null, hostname: null, hash: null, search: '', query: createWithNoPrototype(), pathname: '/example', path: '/example', href: '/example' }, '/example?query=value': { protocol: null, slashes: null, auth: undefined, host: null, port: null, hostname: null, hash: null, search: '?query=value', query: createWithNoPrototype([{ key: 'query', value: 'value' }]), pathname: '/example', path: '/example?query=value', href: '/example?query=value' } }; for (const u in parseTestsWithQueryString) { const actual = url.parse(u, true); const expected = Object.assign(new url.Url(), parseTestsWithQueryString[u]); for (const i in actual) { if (actual[i] === null && expected[i] === undefined) { expected[i] = null; } } const properties = Object.keys(actual).sort(); assert.deepStrictEqual(properties, Object.keys(expected).sort()); properties.forEach((property) => { if (property === 'query') { check(actual[property], expected[property]); } else { assert.deepStrictEqual(actual[property], expected[property]); } }); } // some extra formatting tests, just to verify // that it'll format slightly wonky content to a valid url. var formatTests = { 'http://example.com?': { href: 'http://example.com/?', protocol: 'http:', slashes: true, host: 'example.com', hostname: 'example.com', search: '?', query: {}, pathname: '/' }, 'http://example.com?foo=bar#frag': { href: 'http://example.com/?foo=bar#frag', protocol: 'http:', host: 'example.com', hostname: 'example.com', hash: '#frag', search: '?foo=bar', query: 'foo=bar', pathname: '/' }, 'http://example.com?foo=@bar#frag': { href: 'http://example.com/?foo=@bar#frag', protocol: 'http:', host: 'example.com', hostname: 'example.com', hash: '#frag', search: '?foo=@bar', query: 'foo=@bar', pathname: '/' }, 'http://example.com?foo=/bar/#frag': { href: 'http://example.com/?foo=/bar/#frag', protocol: 'http:', host: 'example.com', hostname: 'example.com', hash: '#frag', search: '?foo=/bar/', query: 'foo=/bar/', pathname: '/' }, 'http://example.com?foo=?bar/#frag': { href: 'http://example.com/?foo=?bar/#frag', protocol: 'http:', host: 'example.com', hostname: 'example.com', hash: '#frag', search: '?foo=?bar/', query: 'foo=?bar/', pathname: '/' }, 'http://example.com#frag=?bar/#frag': { href: 'http://example.com/#frag=?bar/#frag', protocol: 'http:', host: 'example.com', hostname: 'example.com', hash: '#frag=?bar/#frag', pathname: '/' }, 'http://google.com" onload="alert(42)/': { href: 'http://google.com/%22%20onload=%22alert(42)/', protocol: 'http:', host: 'google.com', pathname: '/%22%20onload=%22alert(42)/' }, 'http://a.com/a/b/c?s#h': { href: 'http://a.com/a/b/c?s#h', protocol: 'http', host: 'a.com', pathname: 'a/b/c', hash: 'h', search: 's' }, 'xmpp:isaacschlueter@jabber.org': { href: 'xmpp:isaacschlueter@jabber.org', protocol: 'xmpp:', host: 'jabber.org', auth: 'isaacschlueter', hostname: 'jabber.org' }, 'http://atpass:foo%40bar@127.0.0.1/': { href: 'http://atpass:foo%40bar@127.0.0.1/', auth: 'atpass:foo@bar', hostname: '127.0.0.1', protocol: 'http:', pathname: '/' }, 'http://atslash%2F%40:%2F%40@foo/': { href: 'http://atslash%2F%40:%2F%40@foo/', auth: 'atslash/@:/@', hostname: 'foo', protocol: 'http:', pathname: '/' }, 'svn+ssh://foo/bar': { href: 'svn+ssh://foo/bar', hostname: 'foo', protocol: 'svn+ssh:', pathname: '/bar', slashes: true }, 'dash-test://foo/bar': { href: 'dash-test://foo/bar', hostname: 'foo', protocol: 'dash-test:', pathname: '/bar', slashes: true }, 'dash-test:foo/bar': { href: 'dash-test:foo/bar', hostname: 'foo', protocol: 'dash-test:', pathname: '/bar' }, 'dot.test://foo/bar': { href: 'dot.test://foo/bar', hostname: 'foo', protocol: 'dot.test:', pathname: '/bar', slashes: true }, 'dot.test:foo/bar': { href: 'dot.test:foo/bar', hostname: 'foo', protocol: 'dot.test:', pathname: '/bar' }, // ipv6 support 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { href: 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', protocol: 'coap:', auth: 'u:p', hostname: '::1', port: '61616', pathname: '/.well-known/r', search: 'n=Temperature' }, 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { href: 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', protocol: 'coap', host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', pathname: '/s/stopButton' }, // encode context-specific delimiters in path and query, but do not touch // other non-delimiter chars like `%`. // // `#`,`?` in path '/path/to/%%23%3F+=&.txt?foo=theA1#bar': { href : '/path/to/%%23%3F+=&.txt?foo=theA1#bar', pathname: '/path/to/%#?+=&.txt', query: { foo: 'theA1' }, hash: '#bar' }, // `#`,`?` in path + `#` in query '/path/to/%%23%3F+=&.txt?foo=the%231#bar': { href : '/path/to/%%23%3F+=&.txt?foo=the%231#bar', pathname: '/path/to/%#?+=&.txt', query: { foo: 'the#1' }, hash: '#bar' }, // `?` and `#` in path and search 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': { href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag', protocol: 'http:', hostname: 'ex.com', hash: '#frag', search: '?abc=the#1?&foo=bar', pathname: '/foo?100%m#r', }, // `?` and `#` in search only 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': { href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag', protocol: 'http:', hostname: 'ex.com', hash: '#frag', search: '?abc=the#1?&foo=bar', pathname: '/fooA100%mBr', } }; for (const u in formatTests) { const expect = formatTests[u].href; delete formatTests[u].href; const actual = url.format(u); const actualObj = url.format(formatTests[u]); assert.equal(actual, expect, 'wonky format(' + u + ') == ' + expect + '\nactual:' + actual); assert.equal(actualObj, expect, 'wonky format(' + JSON.stringify(formatTests[u]) + ') == ' + expect + '\nactual: ' + actualObj); } /* [from, path, expected] */ var relativeTests = [ ['/foo/bar/baz', 'quux', '/foo/bar/quux'], ['/foo/bar/baz', 'quux/asdf', '/foo/bar/quux/asdf'], ['/foo/bar/baz', 'quux/baz', '/foo/bar/quux/baz'], ['/foo/bar/baz', '../quux/baz', '/foo/quux/baz'], ['/foo/bar/baz', '/bar', '/bar'], ['/foo/bar/baz/', 'quux', '/foo/bar/baz/quux'], ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'], ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'], ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'], ['/foo', '.', '/'], ['/foo', '..', '/'], ['/foo/', '.', '/foo/'], ['/foo/', '..', '/'], ['/foo/bar', '.', '/foo/'], ['/foo/bar', '..', '/'], ['/foo/bar/', '.', '/foo/bar/'], ['/foo/bar/', '..', '/foo/'], ['foo/bar', '../../../baz', '../../baz'], ['foo/bar/', '../../../baz', '../baz'], ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'], ['http://example.com/b//c//d;p?q#blarg', 'https:/p/a/t/h?s#hash2', 'https://p/a/t/h?s#hash2'], ['http://example.com/b//c//d;p?q#blarg', 'https://u:p@h.com/p/a/t/h?s#hash2', 'https://u:p@h.com/p/a/t/h?s#hash2'], ['http://example.com/b//c//d;p?q#blarg', 'https:/a/b/c/d', 'https://a/b/c/d'], ['http://example.com/b//c//d;p?q#blarg', 'http:#hash2', 'http://example.com/b//c//d;p?q#hash2'], ['http://example.com/b//c//d;p?q#blarg', 'http:/p/a/t/h?s#hash2', 'http://example.com/p/a/t/h?s#hash2'], ['http://example.com/b//c//d;p?q#blarg', 'http://u:p@h.com/p/a/t/h?s#hash2', 'http://u:p@h.com/p/a/t/h?s#hash2'], ['http://example.com/b//c//d;p?q#blarg', 'http:/a/b/c/d', 'http://example.com/a/b/c/d'], ['/foo/bar/baz', '/../etc/passwd', '/etc/passwd'], ['http://localhost', 'file:///Users/foo', 'file:///Users/foo'], ['http://localhost', 'file://foo/Users', 'file://foo/Users'] ]; relativeTests.forEach(function(relativeTest) { const a = url.resolve(relativeTest[0], relativeTest[1]); const e = relativeTest[2]; assert.equal(a, e, 'resolve(' + [relativeTest[0], relativeTest[1]] + ') == ' + e + '\n actual=' + a); }); // https://github.com/joyent/node/issues/568 [ undefined, null, true, false, 0.0, 0, [], {} ].forEach(function(val) { assert.throws(function() { url.parse(val); }, TypeError); }); // // Tests below taken from Chiron // http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js // // Copyright (c) 2002-2008 Kris Kowal // used with permission under MIT License // // Changes marked with @isaacs var bases = [ 'http://a/b/c/d;p?q', 'http://a/b/c/d;p?q=1/2', 'http://a/b/c/d;p=1/2?q', 'fred:///s//a/b/c', 'http:///s//a/b/c' ]; //[to, from, result] var relativeTests2 = [ // http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html ['../c', 'foo:a/b', 'foo:c'], ['foo:.', 'foo:a', 'foo:'], ['/foo/../../../bar', 'zz:abc', 'zz:/bar'], ['/foo/../bar', 'zz:abc', 'zz:/bar'], // @isaacs Disagree. Not how web browsers resolve this. ['foo/../../../bar', 'zz:abc', 'zz:bar'], // ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added ['foo/../bar', 'zz:abc', 'zz:bar'], ['zz:.', 'zz:abc', 'zz:'], ['/.', bases[0], 'http://a/'], ['/.foo', bases[0], 'http://a/.foo'], ['.foo', bases[0], 'http://a/b/c/.foo'], // http://gbiv.com/protocols/uri/test/rel_examples1.html // examples from RFC 2396 ['g:h', bases[0], 'g:h'], ['g', bases[0], 'http://a/b/c/g'], ['./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/'], // changed with RFC 2396bis //('?y', bases[0], 'http://a/b/c/d;p?y'], ['?y', bases[0], 'http://a/b/c/d;p?y'], ['g?y', bases[0], 'http://a/b/c/g?y'], // changed with RFC 2396bis //('#s', bases[0], CURRENT_DOC_URI + '#s'], ['#s', bases[0], 'http://a/b/c/d;p?q#s'], ['g#s', bases[0], 'http://a/b/c/g#s'], ['g?y#s', bases[0], 'http://a/b/c/g?y#s'], [';x', bases[0], 'http://a/b/c/;x'], ['g;x', bases[0], 'http://a/b/c/g;x'], ['g;x?y#s', bases[0], 'http://a/b/c/g;x?y#s'], // changed with RFC 2396bis //('', bases[0], CURRENT_DOC_URI], ['', bases[0], 'http://a/b/c/d;p?q'], ['.', bases[0], 'http://a/b/c/'], ['./', bases[0], 'http://a/b/c/'], ['..', bases[0], 'http://a/b/'], ['../', bases[0], 'http://a/b/'], ['../g', bases[0], 'http://a/b/g'], ['../..', bases[0], 'http://a/'], ['../../', bases[0], 'http://a/'], ['../../g', bases[0], 'http://a/g'], ['../../../g', bases[0], ('http://a/../g', 'http://a/g')], ['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')], // changed with RFC 2396bis //('/./g', bases[0], 'http://a/./g'], ['/./g', bases[0], 'http://a/g'], // changed with RFC 2396bis //('/../g', bases[0], 'http://a/../g'], ['/../g', bases[0], 'http://a/g'], ['g.', bases[0], 'http://a/b/c/g.'], ['.g', bases[0], 'http://a/b/c/.g'], ['g..', bases[0], 'http://a/b/c/g..'], ['..g', bases[0], 'http://a/b/c/..g'], ['./../g', bases[0], 'http://a/b/g'], ['./g/.', bases[0], 'http://a/b/c/g/'], ['g/./h', bases[0], 'http://a/b/c/g/h'], ['g/../h', bases[0], 'http://a/b/c/h'], ['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'], ['g;x=1/../y', bases[0], 'http://a/b/c/y'], ['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'], ['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'], ['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'], ['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'], ['http:g', bases[0], ('http:g', 'http://a/b/c/g')], ['http:', bases[0], ('http:', bases[0])], // not sure where this one originated ['/a/b/c/./../../g', bases[0], 'http://a/a/g'], // http://gbiv.com/protocols/uri/test/rel_examples2.html // slashes in base URI's query args ['g', bases[1], 'http://a/b/c/g'], ['./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/'], // changed in RFC 2396bis //('?y', bases[1], 'http://a/b/c/?y'], ['?y', bases[1], 'http://a/b/c/d;p?y'], ['g?y', bases[1], 'http://a/b/c/g?y'], ['g?y/./x', bases[1], 'http://a/b/c/g?y/./x'], ['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'], ['g#s', bases[1], 'http://a/b/c/g#s'], ['g#s/./x', bases[1], 'http://a/b/c/g#s/./x'], ['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'], ['./', bases[1], 'http://a/b/c/'], ['../', bases[1], 'http://a/b/'], ['../g', bases[1], 'http://a/b/g'], ['../../', bases[1], 'http://a/'], ['../../g', bases[1], 'http://a/g'], // http://gbiv.com/protocols/uri/test/rel_examples3.html // slashes in path params // all of these changed in RFC 2396bis ['g', bases[2], 'http://a/b/c/d;p=1/g'], ['./g', bases[2], 'http://a/b/c/d;p=1/g'], ['g/', bases[2], 'http://a/b/c/d;p=1/g/'], ['g?y', bases[2], 'http://a/b/c/d;p=1/g?y'], [';x', bases[2], 'http://a/b/c/d;p=1/;x'], ['g;x', bases[2], 'http://a/b/c/d;p=1/g;x'], ['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'], ['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'], ['./', bases[2], 'http://a/b/c/d;p=1/'], ['../', bases[2], 'http://a/b/c/'], ['../g', bases[2], 'http://a/b/c/g'], ['../../', bases[2], 'http://a/b/'], ['../../g', bases[2], 'http://a/b/g'], // http://gbiv.com/protocols/uri/test/rel_examples4.html // double and triple slash, unknown scheme ['g:h', bases[3], 'g:h'], ['g', bases[3], 'fred:///s//a/b/g'], ['./g', bases[3], 'fred:///s//a/b/g'], ['g/', bases[3], 'fred:///s//a/b/g/'], ['/g', bases[3], 'fred:///g'], // may change to fred:///s//a/g ['//g', bases[3], 'fred://g'], // may change to fred:///s//g ['//g/x', bases[3], 'fred://g/x'], // may change to fred:///s//g/x ['///g', bases[3], 'fred:///g'], ['./', bases[3], 'fred:///s//a/b/'], ['../', bases[3], 'fred:///s//a/'], ['../g', bases[3], 'fred:///s//a/g'], ['../../', bases[3], 'fred:///s//'], ['../../g', bases[3], 'fred:///s//g'], ['../../../g', bases[3], 'fred:///s/g'], // may change to fred:///s//a/../../../g ['../../../../g', bases[3], 'fred:///g'], // http://gbiv.com/protocols/uri/test/rel_examples5.html // double and triple slash, well-known scheme ['g:h', bases[4], 'g:h'], ['g', bases[4], 'http:///s//a/b/g'], ['./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/x', bases[4], 'http://g/x'], // may change to http:///s//g/x ['///g', bases[4], 'http:///g'], ['./', bases[4], 'http:///s//a/b/'], ['../', bases[4], 'http:///s//a/'], ['../g', bases[4], 'http:///s//a/g'], ['../../', bases[4], 'http:///s//'], ['../../g', bases[4], 'http:///s//g'], // may change to http:///s//a/../../g ['../../../g', bases[4], 'http:///s/g'], // may change to http:///s//a/../../../g ['../../../../g', bases[4], 'http:///g'], // from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py ['bar:abc', 'foo:xyz', 'bar:abc'], ['../abc', 'http://example/x/y/z', 'http://example/x/abc'], ['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'], ['../r', 'http://ex/x/y/z', 'http://ex/x/r'], ['q/r', 'http://ex/x/y', 'http://ex/x/q/r'], ['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'], ['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'], ['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'], ['', 'http://ex/x/y', 'http://ex/x/y'], ['', 'http://ex/x/y/', 'http://ex/x/y/'], ['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'], ['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'], ['#Animal', 'file:/swap/test/animal.rdf', 'file:/swap/test/animal.rdf#Animal'], ['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'], ['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'], ['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'], ['/r', 'file:/ex/x/y/z', 'file:/r'], ['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'], ['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'], ['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'], ['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'], ['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'], ['', 'file:/ex/x/y', 'file:/ex/x/y'], ['', 'file:/ex/x/y/', 'file:/ex/x/y/'], ['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'], ['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'], ['file://meetings.example.com/cal#m1', 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3', 'file://meetings.example.com/cal#m1'], ['file://meetings.example.com/cal#m1', 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3', 'file://meetings.example.com/cal#m1'], ['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'], ['./#', 'file:/some/dir/foo', 'file:/some/dir/#'], // Ryan Lee ['./', 'http://example/x/abc.efg', 'http://example/x/'], // Graham Klyne's tests // http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls // 01-31 are from Connelly's cases // 32-49 ['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'], ['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'], ['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'], ['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'], ['local/qual@domain.org#frag', 'mailto:local', 'mailto:local/qual@domain.org#frag'], ['more/qual2@domain2.org#frag', 'mailto:local/qual1@domain1.org', 'mailto:local/more/qual2@domain2.org#frag'], ['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'], ['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'], ['c/d', 'foo:a/b', 'foo:a/c/d'], ['/c/d', 'foo:a/b', 'foo:/c/d'], ['', 'foo:a/b?c#d', 'foo:a/b?c'], ['b/c', 'foo:a', 'foo:b/c'], ['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'], ['./b/c', 'foo:a', 'foo:b/c'], ['/./b/c', 'foo:a', 'foo:/b/c'], ['../../d', 'foo://a//b/c', 'foo://a/d'], ['.', 'foo:a', 'foo:'], ['..', 'foo:a', 'foo:'], // 50-57[cf. TimBL comments -- // http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html, // http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html) ['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'], ['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'], ['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'], ['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'], ['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'], ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], ['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'], ['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'], // 70-77 ['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'], ['local2@domain2?query2', 'mailto:local1@domain1', 'mailto:local2@domain2?query2'], ['local2@domain2?query2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2?query2'], ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], ['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'], ['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'], ['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'], ['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'], // 82-88 // @isaacs Disagree. Not how browsers do it. // ['http:this', 'http://example.org/base/uri', 'http:this'], // @isaacs Added ['http:this', 'http://example.org/base/uri', 'http://example.org/base/this'], ['http:this', 'http:base', 'http:this'], ['.//g', 'f:/a', 'f://g'], ['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'], ['m2@example.ord/c2@example.org', 'mid:m@example.ord/c@example.org', 'mid:m@example.ord/m2@example.ord/c2@example.org'], ['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'], //changeing auth ['http://diff:auth@www.example.com', 'http://asdf:qwer@www.example.com', 'http://diff:auth@www.example.com/'] ]; relativeTests2.forEach(function(relativeTest) { const a = url.resolve(relativeTest[1], relativeTest[0]); const e = relativeTest[2]; 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)) //format: [from, path, expected] relativeTests.forEach(function(relativeTest) { var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]); var expected = url.parse(relativeTest[2]); assert.deepStrictEqual(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 fundamental problem // url.parse('f:/a') does not have a host // url.resolve('f:/a', './/g') does not have a host because 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 collapse 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]); var expected = url.parse(relativeTest[2]); assert.deepStrictEqual(actual, expected); expected = relativeTest[2]; actual = url.format(actual); assert.equal(actual, expected, 'format(' + relativeTest[1] + ') == ' + expected + '\nactual:' + actual); }); // https://github.com/nodejs/node/pull/1036 var throws = [ undefined, null, true, false, 0, function() {} ]; for (let i = 0; i < throws.length; i++) { assert.throws(function() { url.format(throws[i]); }, TypeError); } assert(url.format('') === ''); assert(url.format({}) === '');