1// Flags: --expose-internals 2'use strict'; 3 4// Tests below are not from WPT. 5 6require('../common'); 7const URL = require('url').URL; 8const assert = require('assert'); 9const urlToOptions = require('internal/url').urlToOptions; 10 11const url = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test'); 12const oldParams = url.searchParams; // For test of [SameObject] 13 14// To retrieve enumerable but not necessarily own properties, 15// we need to use the for-in loop. 16const props = []; 17for (const prop in url) { 18 props.push(prop); 19} 20 21// See: https://url.spec.whatwg.org/#api 22// https://heycam.github.io/webidl/#es-attributes 23// https://heycam.github.io/webidl/#es-stringifier 24const expected = ['toString', 25 'href', 'origin', 'protocol', 26 'username', 'password', 'host', 'hostname', 'port', 27 'pathname', 'search', 'searchParams', 'hash', 'toJSON']; 28 29assert.deepStrictEqual(props, expected); 30 31// `href` is writable (not readonly) and is stringifier 32assert.strictEqual(url.toString(), url.href); 33url.href = 'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test'; 34assert.strictEqual(url.href, 35 'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test'); 36assert.strictEqual(url.toString(), url.href); 37// Return true because it's configurable, but because the properties 38// are defined on the prototype per the spec, the deletion has no effect 39assert.strictEqual((delete url.href), true); 40assert.strictEqual(url.href, 41 'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test'); 42assert.strictEqual(url.searchParams, oldParams); // [SameObject] 43 44// searchParams is readonly. Under strict mode setting a 45// non-writable property should throw. 46// Note: this error message is subject to change in V8 updates 47assert.throws( 48 () => url.origin = 'http://foo.bar.com:22', 49 /^TypeError: Cannot set property origin of \[object URL\] which has only a getter$/ 50); 51assert.strictEqual(url.origin, 'http://foo.bar.com:21'); 52assert.strictEqual(url.toString(), 53 'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test'); 54assert.strictEqual((delete url.origin), true); 55assert.strictEqual(url.origin, 'http://foo.bar.com:21'); 56 57// The following properties should be writable (not readonly) 58url.protocol = 'https:'; 59assert.strictEqual(url.protocol, 'https:'); 60assert.strictEqual(url.toString(), 61 'https://user:pass@foo.bar.com:21/aaa/zzz?l=25#test'); 62assert.strictEqual((delete url.protocol), true); 63assert.strictEqual(url.protocol, 'https:'); 64 65url.username = 'user2'; 66assert.strictEqual(url.username, 'user2'); 67assert.strictEqual(url.toString(), 68 'https://user2:pass@foo.bar.com:21/aaa/zzz?l=25#test'); 69assert.strictEqual((delete url.username), true); 70assert.strictEqual(url.username, 'user2'); 71 72url.password = 'pass2'; 73assert.strictEqual(url.password, 'pass2'); 74assert.strictEqual(url.toString(), 75 'https://user2:pass2@foo.bar.com:21/aaa/zzz?l=25#test'); 76assert.strictEqual((delete url.password), true); 77assert.strictEqual(url.password, 'pass2'); 78 79url.host = 'foo.bar.net:22'; 80assert.strictEqual(url.host, 'foo.bar.net:22'); 81assert.strictEqual(url.toString(), 82 'https://user2:pass2@foo.bar.net:22/aaa/zzz?l=25#test'); 83assert.strictEqual((delete url.host), true); 84assert.strictEqual(url.host, 'foo.bar.net:22'); 85 86url.hostname = 'foo.bar.org'; 87assert.strictEqual(url.hostname, 'foo.bar.org'); 88assert.strictEqual(url.toString(), 89 'https://user2:pass2@foo.bar.org:22/aaa/zzz?l=25#test'); 90assert.strictEqual((delete url.hostname), true); 91assert.strictEqual(url.hostname, 'foo.bar.org'); 92 93url.port = '23'; 94assert.strictEqual(url.port, '23'); 95assert.strictEqual(url.toString(), 96 'https://user2:pass2@foo.bar.org:23/aaa/zzz?l=25#test'); 97assert.strictEqual((delete url.port), true); 98assert.strictEqual(url.port, '23'); 99 100url.pathname = '/aaa/bbb'; 101assert.strictEqual(url.pathname, '/aaa/bbb'); 102assert.strictEqual(url.toString(), 103 'https://user2:pass2@foo.bar.org:23/aaa/bbb?l=25#test'); 104assert.strictEqual((delete url.pathname), true); 105assert.strictEqual(url.pathname, '/aaa/bbb'); 106 107url.search = '?k=99'; 108assert.strictEqual(url.search, '?k=99'); 109assert.strictEqual(url.toString(), 110 'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#test'); 111assert.strictEqual((delete url.search), true); 112assert.strictEqual(url.search, '?k=99'); 113 114url.hash = '#abcd'; 115assert.strictEqual(url.hash, '#abcd'); 116assert.strictEqual(url.toString(), 117 'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#abcd'); 118assert.strictEqual((delete url.hash), true); 119assert.strictEqual(url.hash, '#abcd'); 120 121// searchParams is readonly. Under strict mode setting a 122// non-writable property should throw. 123// Note: this error message is subject to change in V8 updates 124assert.throws( 125 () => url.searchParams = '?k=88', 126 /^TypeError: Cannot set property searchParams of \[object URL\] which has only a getter$/ 127); 128assert.strictEqual(url.searchParams, oldParams); 129assert.strictEqual(url.toString(), 130 'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#abcd'); 131assert.strictEqual((delete url.searchParams), true); 132assert.strictEqual(url.searchParams, oldParams); 133 134// Test urlToOptions 135{ 136 const urlObj = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test'); 137 const opts = urlToOptions(urlObj); 138 assert.strictEqual(opts instanceof URL, false); 139 assert.strictEqual(opts.protocol, 'http:'); 140 assert.strictEqual(opts.auth, 'user:pass'); 141 assert.strictEqual(opts.hostname, 'foo.bar.com'); 142 assert.strictEqual(opts.port, 21); 143 assert.strictEqual(opts.path, '/aaa/zzz?l=24'); 144 assert.strictEqual(opts.pathname, '/aaa/zzz'); 145 assert.strictEqual(opts.search, '?l=24'); 146 assert.strictEqual(opts.hash, '#test'); 147 148 const { hostname } = urlToOptions(new URL('http://[::1]:21')); 149 assert.strictEqual(hostname, '::1'); 150 151 // If a WHATWG URL object is copied, it is possible that the resulting copy 152 // contains the Symbols that Node uses for brand checking, but not the data 153 // properties, which are getters. Verify that urlToOptions() can handle such 154 // a case. 155 const copiedUrlObj = { ...urlObj }; 156 const copiedOpts = urlToOptions(copiedUrlObj); 157 assert.strictEqual(copiedOpts instanceof URL, false); 158 assert.strictEqual(copiedOpts.protocol, undefined); 159 assert.strictEqual(copiedOpts.auth, undefined); 160 assert.strictEqual(copiedOpts.hostname, undefined); 161 assert.strictEqual(copiedOpts.port, NaN); 162 assert.strictEqual(copiedOpts.path, ''); 163 assert.strictEqual(copiedOpts.pathname, undefined); 164 assert.strictEqual(copiedOpts.search, undefined); 165 assert.strictEqual(copiedOpts.hash, undefined); 166 assert.strictEqual(copiedOpts.href, undefined); 167} 168 169// Test special origins 170[ 171 { expected: 'https://whatwg.org', 172 url: 'blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f' }, 173 { expected: 'ftp://example.org', url: 'ftp://example.org/foo' }, 174 { expected: 'gopher://gopher.quux.org', url: 'gopher://gopher.quux.org/1/' }, 175 { expected: 'http://example.org', url: 'http://example.org/foo' }, 176 { expected: 'https://example.org', url: 'https://example.org/foo' }, 177 { expected: 'ws://example.org', url: 'ws://example.org/foo' }, 178 { expected: 'wss://example.org', url: 'wss://example.org/foo' }, 179 { expected: 'null', url: 'file:///tmp/mock/path' }, 180 { expected: 'null', url: 'npm://nodejs/rules' } 181].forEach((test) => { 182 assert.strictEqual(new URL(test.url).origin, test.expected); 183}); 184