text: // Flags: --expose-internals
  'use strict';
  
  // Tests below are not from WPT.
  
  const common = require('../common');
  const URL = require('url').URL;
  const assert = require('assert');
  const urlToOptions = require('internal/url').urlToOptions;
  
  const url = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test');
  const oldParams = url.searchParams;  // for test of [SameObject]
  
  // To retrieve enumerable but not necessarily own properties,
  // we need to use the for-in loop.
  const props = [];
  for (const prop in url) {
    props.push(prop);
  }
  
  // See: https://url.spec.whatwg.org/#api
  // https://heycam.github.io/webidl/#es-attributes
  // https://heycam.github.io/webidl/#es-stringifier
  const expected = ['toString',
                    'href', 'origin', 'protocol',
                    'username', 'password', 'host', 'hostname', 'port',
                    'pathname', 'search', 'searchParams', 'hash', 'toJSON'];
  
  assert.deepStrictEqual(props, expected);
  
  // href is writable (not readonly) and is stringifier
  assert.strictEqual(url.toString(), url.href);
  url.href = 'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test';
  assert.strictEqual(url.href,
                     'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
  assert.strictEqual(url.toString(), url.href);
  // Return true because it's configurable, but because the properties
  // are defined on the prototype per the spec, the deletion has no effect
  assert.strictEqual((delete url.href), true);
  assert.strictEqual(url.href,
                     'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
  assert.strictEqual(url.searchParams, oldParams);  // [SameObject]
  
  // searchParams is readonly. Under strict mode setting a
  // non-writable property should throw.
  // Note: this error message is subject to change in V8 updates
  assert.throws(
    () => url.origin = 'http://foo.bar.com:22',
    common.engineSpecificMessage({
      v8: /^TypeError: Cannot set property origin of \[object URL\] which has only a getter$/,
      chakracore: /^TypeError: Assignment to read-only properties is not allowed in strict mode$/
    })
  );
  assert.strictEqual(url.origin, 'http://foo.bar.com:21');
  assert.strictEqual(url.toString(),
                     'http://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.origin), true);
  assert.strictEqual(url.origin, 'http://foo.bar.com:21');
  
  // The following properties should be writable (not readonly)
  url.protocol = 'https:';
  assert.strictEqual(url.protocol, 'https:');
  assert.strictEqual(url.toString(),
                     'https://user:pass@foo.bar.com:21/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.protocol), true);
  assert.strictEqual(url.protocol, 'https:');
  
  url.username = 'user2';
  assert.strictEqual(url.username, 'user2');
  assert.strictEqual(url.toString(),
                     'https://user2:pass@foo.bar.com:21/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.username), true);
  assert.strictEqual(url.username, 'user2');
  
  url.password = 'pass2';
  assert.strictEqual(url.password, 'pass2');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.com:21/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.password), true);
  assert.strictEqual(url.password, 'pass2');
  
  url.host = 'foo.bar.net:22';
  assert.strictEqual(url.host, 'foo.bar.net:22');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.net:22/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.host), true);
  assert.strictEqual(url.host, 'foo.bar.net:22');
  
  url.hostname = 'foo.bar.org';
  assert.strictEqual(url.hostname, 'foo.bar.org');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.org:22/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.hostname), true);
  assert.strictEqual(url.hostname, 'foo.bar.org');
  
  url.port = '23';
  assert.strictEqual(url.port, '23');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.org:23/aaa/zzz?l=25#test');
  assert.strictEqual((delete url.port), true);
  assert.strictEqual(url.port, '23');
  
  url.pathname = '/aaa/bbb';
  assert.strictEqual(url.pathname, '/aaa/bbb');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.org:23/aaa/bbb?l=25#test');
  assert.strictEqual((delete url.pathname), true);
  assert.strictEqual(url.pathname, '/aaa/bbb');
  
  url.search = '?k=99';
  assert.strictEqual(url.search, '?k=99');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#test');
  assert.strictEqual((delete url.search), true);
  assert.strictEqual(url.search, '?k=99');
  
  url.hash = '#abcd';
  assert.strictEqual(url.hash, '#abcd');
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#abcd');
  assert.strictEqual((delete url.hash), true);
  assert.strictEqual(url.hash, '#abcd');
  
  // searchParams is readonly. Under strict mode setting a
  // non-writable property should throw.
  // Note: this error message is subject to change in V8 updates
  assert.throws(
    () => url.searchParams = '?k=88',
    common.engineSpecificMessage({
      v8: /^TypeError: Cannot set property searchParams of \[object URL\] which has only a getter$/,
      chakracore: /^TypeError: Assignment to read-only properties is not allowed in strict mode$/
    })
  );
  assert.strictEqual(url.searchParams, oldParams);
  assert.strictEqual(url.toString(),
                     'https://user2:pass2@foo.bar.org:23/aaa/bbb?k=99#abcd');
  assert.strictEqual((delete url.searchParams), true);
  assert.strictEqual(url.searchParams, oldParams);
  
  // Test urlToOptions
  {
    const opts =
      urlToOptions(new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test'));
    assert.strictEqual(opts instanceof URL, false);
    assert.strictEqual(opts.protocol, 'http:');
    assert.strictEqual(opts.auth, 'user:pass');
    assert.strictEqual(opts.hostname, 'foo.bar.com');
    assert.strictEqual(opts.port, 21);
    assert.strictEqual(opts.path, '/aaa/zzz?l=24');
    assert.strictEqual(opts.pathname, '/aaa/zzz');
    assert.strictEqual(opts.search, '?l=24');
    assert.strictEqual(opts.hash, '#test');
  
    const { hostname } = urlToOptions(new URL('http://[::1]:21'));
    assert.strictEqual(hostname, '::1');
  }
  
  // Test special origins
  [
    { expected: 'https://whatwg.org',
      url: 'blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f' },
    { expected: 'ftp://example.org', url: 'ftp://example.org/foo' },
    { expected: 'gopher://gopher.quux.org', url: 'gopher://gopher.quux.org/1/' },
    { expected: 'http://example.org', url: 'http://example.org/foo' },
    { expected: 'https://example.org', url: 'https://example.org/foo' },
    { expected: 'ws://example.org', url: 'ws://example.org/foo' },
    { expected: 'wss://example.org', url: 'wss://example.org/foo' },
    { expected: 'null', url: 'file:///tmp/mock/path' },
    { expected: 'null', url: 'npm://nodejs/rules' }
  ].forEach((test) => {
    assert.strictEqual(new URL(test.url).origin, test.expected);
  });
  