• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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