mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 23:18:47 +00:00
Compare commits
2 Commits
riskymh/sq
...
url
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a93ce9f4a6 | ||
|
|
1de386bca7 |
1111
src/js/node/url.ts
1111
src/js/node/url.ts
File diff suppressed because it is too large
Load Diff
273
test/js/node/test/parallel/test-punycode.js
Normal file
273
test/js/node/test/parallel/test-punycode.js
Normal file
@@ -0,0 +1,273 @@
|
||||
// Flags: --pending-deprecation
|
||||
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
const punycodeWarning =
|
||||
'The `punycode` module is deprecated. Please use a userland alternative ' +
|
||||
'instead.';
|
||||
//common.expectWarning('DeprecationWarning', punycodeWarning, 'DEP0040');
|
||||
|
||||
const punycode = require('punycode');
|
||||
const assert = require('assert');
|
||||
|
||||
assert.strictEqual(punycode.encode('ü'), 'tda');
|
||||
assert.strictEqual(punycode.encode('Goethe'), 'Goethe-');
|
||||
assert.strictEqual(punycode.encode('Bücher'), 'Bcher-kva');
|
||||
assert.strictEqual(
|
||||
punycode.encode(
|
||||
'Willst du die Blüthe des frühen, die Früchte des späteren Jahres'
|
||||
),
|
||||
'Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal'
|
||||
);
|
||||
assert.strictEqual(punycode.encode('日本語'), 'wgv71a119e');
|
||||
assert.strictEqual(punycode.encode('𩸽'), 'x73l');
|
||||
|
||||
assert.strictEqual(punycode.decode('tda'), 'ü');
|
||||
assert.strictEqual(punycode.decode('Goethe-'), 'Goethe');
|
||||
assert.strictEqual(punycode.decode('Bcher-kva'), 'Bücher');
|
||||
assert.strictEqual(
|
||||
punycode.decode(
|
||||
'Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal'
|
||||
),
|
||||
'Willst du die Blüthe des frühen, die Früchte des späteren Jahres'
|
||||
);
|
||||
assert.strictEqual(punycode.decode('wgv71a119e'), '日本語');
|
||||
assert.strictEqual(punycode.decode('x73l'), '𩸽');
|
||||
assert.throws(() => {
|
||||
punycode.decode(' ');
|
||||
}, /^RangeError: Invalid input$/);
|
||||
assert.throws(() => {
|
||||
punycode.decode('α-');
|
||||
}, /^RangeError: Illegal input >= 0x80 \(not a basic code point\)$/);
|
||||
assert.throws(() => {
|
||||
punycode.decode('あ');
|
||||
}, /^RangeError: Invalid input$/);
|
||||
|
||||
// http://tools.ietf.org/html/rfc3492#section-7.1
|
||||
const tests = [
|
||||
// (A) Arabic (Egyptian)
|
||||
{
|
||||
encoded: 'egbpdaj6bu4bxfgehfvwxn',
|
||||
decoded: '\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644\u0645' +
|
||||
'\u0648\u0634\u0639\u0631\u0628\u064A\u061F'
|
||||
},
|
||||
|
||||
// (B) Chinese (simplified)
|
||||
{
|
||||
encoded: 'ihqwcrb4cv8a8dqg056pqjye',
|
||||
decoded: '\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587'
|
||||
},
|
||||
|
||||
// (C) Chinese (traditional)
|
||||
{
|
||||
encoded: 'ihqwctvzc91f659drss3x8bo0yb',
|
||||
decoded: '\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587'
|
||||
},
|
||||
|
||||
// (D) Czech: Pro<ccaron>prost<ecaron>nemluv<iacute><ccaron>esky
|
||||
{
|
||||
encoded: 'Proprostnemluvesky-uyb24dma41a',
|
||||
decoded: '\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074\u011B' +
|
||||
'\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D\u0065\u0073\u006B\u0079'
|
||||
},
|
||||
|
||||
// (E) Hebrew
|
||||
{
|
||||
encoded: '4dbcagdahymbxekheh6e0a7fei0b',
|
||||
decoded: '\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8\u05DC' +
|
||||
'\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2\u05D1\u05E8\u05D9\u05EA'
|
||||
},
|
||||
|
||||
// (F) Hindi (Devanagari)
|
||||
{
|
||||
encoded: 'i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd',
|
||||
decoded: '\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D\u0926' +
|
||||
'\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939\u0940\u0902\u092C' +
|
||||
'\u094B\u0932\u0938\u0915\u0924\u0947\u0939\u0948\u0902'
|
||||
},
|
||||
|
||||
// (G) Japanese (kanji and hiragana)
|
||||
{
|
||||
encoded: 'n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa',
|
||||
decoded: '\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092\u8A71' +
|
||||
'\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B'
|
||||
},
|
||||
|
||||
// (H) Korean (Hangul syllables)
|
||||
{
|
||||
encoded: '989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879' +
|
||||
'ccm6fea98c',
|
||||
decoded: '\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774\uD55C' +
|
||||
'\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74\uC5BC\uB9C8\uB098' +
|
||||
'\uC88B\uC744\uAE4C'
|
||||
},
|
||||
|
||||
// (I) Russian (Cyrillic)
|
||||
{
|
||||
encoded: 'b1abfaaepdrnnbgefbadotcwatmq2g4l',
|
||||
decoded: '\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E\u043D' +
|
||||
'\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440\u044F\u0442\u043F' +
|
||||
'\u043E\u0440\u0443\u0441\u0441\u043A\u0438'
|
||||
},
|
||||
|
||||
// (J) Spanish: Porqu<eacute>nopuedensimplementehablarenEspa<ntilde>ol
|
||||
{
|
||||
encoded: 'PorqunopuedensimplementehablarenEspaol-fmd56a',
|
||||
decoded: '\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070\u0075' +
|
||||
'\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070\u006C\u0065\u006D' +
|
||||
'\u0065\u006E\u0074\u0065\u0068\u0061\u0062\u006C\u0061\u0072\u0065' +
|
||||
'\u006E\u0045\u0073\u0070\u0061\u00F1\u006F\u006C'
|
||||
},
|
||||
|
||||
// (K) Vietnamese: T<adotbelow>isaoh<odotbelow>kh<ocirc>ngth
|
||||
// <ecirchookabove>ch<ihookabove>n<oacute>iti<ecircacute>ngVi<ecircdotbelow>t
|
||||
{
|
||||
encoded: 'TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g',
|
||||
decoded: '\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B\u0068' +
|
||||
'\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068\u1EC9\u006E\u00F3' +
|
||||
'\u0069\u0074\u0069\u1EBF\u006E\u0067\u0056\u0069\u1EC7\u0074'
|
||||
},
|
||||
|
||||
// (L) 3<nen>B<gumi><kinpachi><sensei>
|
||||
{
|
||||
encoded: '3B-ww4c5e180e575a65lsy2b',
|
||||
decoded: '\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F'
|
||||
},
|
||||
|
||||
// (M) <amuro><namie>-with-SUPER-MONKEYS
|
||||
{
|
||||
encoded: '-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n',
|
||||
decoded: '\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074\u0068' +
|
||||
'\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D\u004F\u004E\u004B' +
|
||||
'\u0045\u0059\u0053'
|
||||
},
|
||||
|
||||
// (N) Hello-Another-Way-<sorezore><no><basho>
|
||||
{
|
||||
encoded: 'Hello-Another-Way--fc4qua05auwb3674vfr0b',
|
||||
decoded: '\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F\u0074' +
|
||||
'\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D\u305D\u308C\u305E' +
|
||||
'\u308C\u306E\u5834\u6240'
|
||||
},
|
||||
|
||||
// (O) <hitotsu><yane><no><shita>2
|
||||
{
|
||||
encoded: '2-u9tlzr9756bt3uc0v',
|
||||
decoded: '\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032'
|
||||
},
|
||||
|
||||
// (P) Maji<de>Koi<suru>5<byou><mae>
|
||||
{
|
||||
encoded: 'MajiKoi5-783gue6qz075azm5e',
|
||||
decoded: '\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059\u308B' +
|
||||
'\u0035\u79D2\u524D'
|
||||
},
|
||||
|
||||
// (Q) <pafii>de<runba>
|
||||
{
|
||||
encoded: 'de-jg4avhby1noc0d',
|
||||
decoded: '\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0'
|
||||
},
|
||||
|
||||
// (R) <sono><supiido><de>
|
||||
{
|
||||
encoded: 'd9juau41awczczp',
|
||||
decoded: '\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067'
|
||||
},
|
||||
|
||||
// (S) -> $1.00 <-
|
||||
{
|
||||
encoded: '-> $1.00 <--',
|
||||
decoded: '\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020\u003C' +
|
||||
'\u002D'
|
||||
},
|
||||
];
|
||||
|
||||
let errors = 0;
|
||||
const handleError = (error, name) => {
|
||||
console.error(
|
||||
`FAIL: ${name} expected ${error.expected}, got ${error.actual}`
|
||||
);
|
||||
errors++;
|
||||
};
|
||||
|
||||
const regexNonASCII = /[^\x20-\x7E]/;
|
||||
const testBattery = {
|
||||
encode: (test) => assert.strictEqual(
|
||||
punycode.encode(test.decoded),
|
||||
test.encoded
|
||||
),
|
||||
decode: (test) => assert.strictEqual(
|
||||
punycode.decode(test.encoded),
|
||||
test.decoded
|
||||
),
|
||||
toASCII: (test) => assert.strictEqual(
|
||||
punycode.toASCII(test.decoded),
|
||||
regexNonASCII.test(test.decoded) ?
|
||||
`xn--${test.encoded}` :
|
||||
test.decoded
|
||||
),
|
||||
toUnicode: (test) => assert.strictEqual(
|
||||
punycode.toUnicode(
|
||||
regexNonASCII.test(test.decoded) ?
|
||||
`xn--${test.encoded}` :
|
||||
test.decoded
|
||||
),
|
||||
regexNonASCII.test(test.decoded) ?
|
||||
test.decoded.toLowerCase() :
|
||||
test.decoded
|
||||
)
|
||||
};
|
||||
|
||||
tests.forEach((testCase) => {
|
||||
Object.keys(testBattery).forEach((key) => {
|
||||
try {
|
||||
testBattery[key](testCase);
|
||||
} catch (error) {
|
||||
handleError(error, key);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// BMP code point
|
||||
assert.strictEqual(punycode.ucs2.encode([0x61]), 'a');
|
||||
// Supplementary code point (surrogate pair)
|
||||
assert.strictEqual(punycode.ucs2.encode([0x1D306]), '\uD834\uDF06');
|
||||
// high surrogate
|
||||
assert.strictEqual(punycode.ucs2.encode([0xD800]), '\uD800');
|
||||
// High surrogate followed by non-surrogates
|
||||
assert.strictEqual(punycode.ucs2.encode([0xD800, 0x61, 0x62]), '\uD800ab');
|
||||
// low surrogate
|
||||
assert.strictEqual(punycode.ucs2.encode([0xDC00]), '\uDC00');
|
||||
// Low surrogate followed by non-surrogates
|
||||
assert.strictEqual(punycode.ucs2.encode([0xDC00, 0x61, 0x62]), '\uDC00ab');
|
||||
|
||||
assert.strictEqual(errors, 0);
|
||||
|
||||
// test map domain
|
||||
assert.strictEqual(punycode.toASCII('Bücher@日本語.com'),
|
||||
'Bücher@xn--wgv71a119e.com');
|
||||
assert.strictEqual(punycode.toUnicode('Bücher@xn--wgv71a119e.com'),
|
||||
'Bücher@日本語.com');
|
||||
@@ -1,14 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasIntl)
|
||||
common.skip('missing Intl');
|
||||
const { hasIntl } = require('../common');
|
||||
|
||||
const strictEqual = require('assert').strictEqual;
|
||||
const url = require('url');
|
||||
|
||||
const domainToASCII = url.domainToASCII;
|
||||
const domainToUnicode = url.domainToUnicode;
|
||||
const { strictEqual } = require('node:assert');
|
||||
const { domainToASCII, domainToUnicode } = require('node:url');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const domainWithASCII = [
|
||||
['ıíd', 'xn--d-iga7r'],
|
||||
@@ -21,11 +17,11 @@ const domainWithASCII = [
|
||||
['भारत.org', 'xn--h2brj9c.org'],
|
||||
];
|
||||
|
||||
domainWithASCII.forEach((pair) => {
|
||||
const domain = pair[0];
|
||||
const ascii = pair[1];
|
||||
const domainConvertedToASCII = domainToASCII(domain);
|
||||
strictEqual(domainConvertedToASCII, ascii);
|
||||
const asciiConvertedToUnicode = domainToUnicode(ascii);
|
||||
strictEqual(asciiConvertedToUnicode, domain);
|
||||
test('domainToASCII and domainToUnicode', { skip: !hasIntl }, () => {
|
||||
for (const [domain, ascii] of domainWithASCII) {
|
||||
const domainConvertedToASCII = domainToASCII(domain);
|
||||
strictEqual(domainConvertedToASCII, ascii);
|
||||
const asciiConvertedToUnicode = domainToUnicode(ascii);
|
||||
strictEqual(asciiConvertedToUnicode, domain);
|
||||
}
|
||||
});
|
||||
|
||||
177
test/js/node/test/parallel/test-url-fileurltopath.js
Normal file
177
test/js/node/test/parallel/test-url-fileurltopath.js
Normal file
@@ -0,0 +1,177 @@
|
||||
'use strict';
|
||||
const { isWindows } = require('../common');
|
||||
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const url = require('node:url');
|
||||
|
||||
test('invalid arguments', () => {
|
||||
for (const arg of [null, undefined, 1, {}, true]) {
|
||||
assert.throws(() => url.fileURLToPath(arg), {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('input must be a file URL', () => {
|
||||
assert.throws(() => url.fileURLToPath('https://a/b/c'), {
|
||||
code: 'ERR_INVALID_URL_SCHEME'
|
||||
});
|
||||
});
|
||||
|
||||
test('fileURLToPath with host', () => {
|
||||
const withHost = new URL('file://host/a');
|
||||
|
||||
if (isWindows) {
|
||||
assert.strictEqual(url.fileURLToPath(withHost), '\\\\host\\a');
|
||||
} else {
|
||||
assert.throws(() => url.fileURLToPath(withHost), {
|
||||
code: 'ERR_INVALID_FILE_URL_HOST'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('fileURLToPath with invalid path', () => {
|
||||
if (isWindows) {
|
||||
assert.throws(() => url.fileURLToPath('file:///C:/a%2F/'), {
|
||||
code: 'ERR_INVALID_FILE_URL_PATH'
|
||||
});
|
||||
assert.throws(() => url.fileURLToPath('file:///C:/a%5C/'), {
|
||||
code: 'ERR_INVALID_FILE_URL_PATH'
|
||||
});
|
||||
assert.throws(() => url.fileURLToPath('file:///?:/'), {
|
||||
code: 'ERR_INVALID_FILE_URL_PATH'
|
||||
});
|
||||
} else {
|
||||
assert.throws(() => url.fileURLToPath('file:///a%2F/'), {
|
||||
code: 'ERR_INVALID_FILE_URL_PATH'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const windowsTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: 'C:\\foo', fileURL: 'file:///C:/foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', fileURL: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', fileURL: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', fileURL: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', fileURL: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', fileURL: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', fileURL: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', fileURL: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', fileURL: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', fileURL: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', fileURL: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', fileURL: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', fileURL: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', fileURL: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', fileURL: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', fileURL: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', fileURL: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', fileURL: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', fileURL: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: 'C:\\€', fileURL: 'file:///C:/%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', fileURL: 'file:///C:/%F0%9F%9A%80' },
|
||||
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
|
||||
{ path: '\\\\nas\\My Docs\\File.doc', fileURL: 'file://nas/My%20Docs/File.doc' },
|
||||
];
|
||||
|
||||
const posixTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: '/foo', fileURL: 'file:///foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: '/FOO', fileURL: 'file:///FOO' },
|
||||
// dir
|
||||
{ path: '/dir/foo', fileURL: 'file:///dir/foo' },
|
||||
// trailing separator
|
||||
{ path: '/dir/', fileURL: 'file:///dir/' },
|
||||
// dot
|
||||
{ path: '/foo.mjs', fileURL: 'file:///foo.mjs' },
|
||||
// space
|
||||
{ path: '/foo bar', fileURL: 'file:///foo%20bar' },
|
||||
// question mark
|
||||
{ path: '/foo?bar', fileURL: 'file:///foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: '/foo#bar', fileURL: 'file:///foo%23bar' },
|
||||
// ampersand
|
||||
{ path: '/foo&bar', fileURL: 'file:///foo&bar' },
|
||||
// equals
|
||||
{ path: '/foo=bar', fileURL: 'file:///foo=bar' },
|
||||
// colon
|
||||
{ path: '/foo:bar', fileURL: 'file:///foo:bar' },
|
||||
// semicolon
|
||||
{ path: '/foo;bar', fileURL: 'file:///foo;bar' },
|
||||
// percent
|
||||
{ path: '/foo%bar', fileURL: 'file:///foo%25bar' },
|
||||
// backslash
|
||||
{ path: '/foo\\bar', fileURL: 'file:///foo%5Cbar' },
|
||||
// backspace
|
||||
{ path: '/foo\bbar', fileURL: 'file:///foo%08bar' },
|
||||
// tab
|
||||
{ path: '/foo\tbar', fileURL: 'file:///foo%09bar' },
|
||||
// newline
|
||||
{ path: '/foo\nbar', fileURL: 'file:///foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: '/foo\rbar', fileURL: 'file:///foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: '/fóóbàr', fileURL: 'file:///f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: '/€', fileURL: 'file:///%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: '/🚀', fileURL: 'file:///%F0%9F%9A%80' },
|
||||
];
|
||||
|
||||
test('fileURLToPath with windows path', { skip: !isWindows }, () => {
|
||||
|
||||
for (const { path, fileURL } of windowsTestCases) {
|
||||
const fromString = url.fileURLToPath(fileURL, { windows: true });
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL), { windows: true });
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
});
|
||||
|
||||
test('fileURLToPath with posix path', { skip: isWindows }, () => {
|
||||
for (const { path, fileURL } of posixTestCases) {
|
||||
const fromString = url.fileURLToPath(fileURL, { windows: false });
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL), { windows: false });
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
});
|
||||
|
||||
const defaultTestCases = isWindows ? windowsTestCases : posixTestCases;
|
||||
|
||||
test('options is null', () => {
|
||||
const whenNullActual = url.fileURLToPath(new URL(defaultTestCases[0].fileURL), null);
|
||||
assert.strictEqual(whenNullActual, defaultTestCases[0].path);
|
||||
});
|
||||
|
||||
test('defaultTestCases', () => {
|
||||
for (const { path, fileURL } of defaultTestCases) {
|
||||
const fromString = url.fileURLToPath(fileURL);
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL));
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
});
|
||||
30
test/js/node/test/parallel/test-url-format-invalid-input.js
Normal file
30
test/js/node/test/parallel/test-url-format-invalid-input.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
|
||||
const assert = require('node:assert');
|
||||
const url = require('node:url');
|
||||
const { test } = require('node:test');
|
||||
|
||||
test('format invalid input', () => {
|
||||
const throwsObjsAndReportTypes = [
|
||||
undefined,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
function() {},
|
||||
Symbol('foo'),
|
||||
];
|
||||
|
||||
for (const urlObject of throwsObjsAndReportTypes) {
|
||||
assert.throws(() => {
|
||||
url.format(urlObject);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
}
|
||||
assert.strictEqual(url.format(''), '');
|
||||
assert.strictEqual(url.format({}), '');
|
||||
});
|
||||
@@ -1,147 +1,149 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasIntl)
|
||||
common.skip('missing Intl');
|
||||
const { hasIntl } = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const url = require('url');
|
||||
const assert = require('node:assert');
|
||||
const url = require('node:url');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const myURL = new URL('http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c');
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
test('should format', { skip: !hasIntl }, () => {
|
||||
assert.strictEqual(
|
||||
url.format(myURL),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, {}),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, {}),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
});
|
||||
|
||||
{
|
||||
[true, 1, 'test', Infinity].forEach((value) => {
|
||||
test('handle invalid arguments', { skip: !hasIntl }, () => {
|
||||
for (const value of [true, 1, 'test', Infinity]) {
|
||||
assert.throws(
|
||||
() => url.format(myURL, value),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options" argument must be of type object.' +
|
||||
common.invalidArgTypeHelper(value)
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Any falsy value other than undefined will be treated as false.
|
||||
// Any truthy value will be treated as true.
|
||||
test('any falsy value other than undefined will be treated as false', { skip: !hasIntl }, () => {
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: false }),
|
||||
'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: false }),
|
||||
'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: '' }),
|
||||
'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: '' }),
|
||||
'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: 0 }),
|
||||
'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: 0 }),
|
||||
'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: 1 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: 1 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: {} }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { auth: {} }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: false }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: false }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: '' }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: '' }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: 0 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: 0 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: 1 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: 1 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: {} }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { fragment: {} }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: false }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: false }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: '' }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: '' }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: 0 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: 0 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: 1 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: 1 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: {} }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { search: {} }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: true }),
|
||||
'http://user:pass@理容ナカムラ.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: true }),
|
||||
'http://user:pass@理容ナカムラ.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: 1 }),
|
||||
'http://user:pass@理容ナカムラ.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: 1 }),
|
||||
'http://user:pass@理容ナカムラ.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: {} }),
|
||||
'http://user:pass@理容ナカムラ.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: {} }),
|
||||
'http://user:pass@理容ナカムラ.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: false }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: false }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: 0 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(myURL, { unicode: 0 }),
|
||||
'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'
|
||||
);
|
||||
test('should format with unicode: true', { skip: !hasIntl }, () => {
|
||||
assert.strictEqual(
|
||||
url.format(new URL('http://user:pass@xn--0zwm56d.com:8080/path'), { unicode: true }),
|
||||
'http://user:pass@测试.com:8080/path'
|
||||
);
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(new URL('http://user:pass@xn--0zwm56d.com:8080/path'), { unicode: true }),
|
||||
'http://user:pass@测试.com:8080/path'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
url.format(new URL('tel:123')),
|
||||
url.format(new URL('tel:123'), { unicode: true })
|
||||
);
|
||||
test('should format tel: prefix', { skip: !hasIntl }, () => {
|
||||
assert.strictEqual(
|
||||
url.format(new URL('tel:123')),
|
||||
url.format(new URL('tel:123'), { unicode: true })
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,277 +1,278 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const url = require('url');
|
||||
|
||||
if (!common.hasIntl)
|
||||
common.skip('missing Intl');
|
||||
const { hasIntl } = require('../common');
|
||||
|
||||
// Formatting tests to verify that it'll format slightly wonky content to a
|
||||
// valid URL.
|
||||
const 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'
|
||||
},
|
||||
'http://[::]/': {
|
||||
href: 'http://[::]/',
|
||||
protocol: 'http:',
|
||||
hostname: '[::]',
|
||||
pathname: '/'
|
||||
},
|
||||
const assert = require('node:assert');
|
||||
const url = require('node:url');
|
||||
const { test } = require('node:test');
|
||||
|
||||
// Encode context-specific delimiters in path and query, but do not touch
|
||||
// other non-delimiter chars like `%`.
|
||||
// <https://github.com/nodejs/node-v0.x-archive/issues/4082>
|
||||
|
||||
// `#`,`?` 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'
|
||||
test('format slightly wonky content to a valid URL', { skip: !hasIntl }, () => {
|
||||
const formatTests = {
|
||||
'http://example.com?': {
|
||||
href: 'http://example.com/?',
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: 'example.com',
|
||||
hostname: 'example.com',
|
||||
search: '?',
|
||||
query: {},
|
||||
pathname: '/'
|
||||
},
|
||||
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'
|
||||
'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: '/'
|
||||
},
|
||||
hash: '#bar'
|
||||
},
|
||||
|
||||
// `#` in path end + `#` in query
|
||||
'/path/to/%%23?foo=the%231#bar': {
|
||||
href: '/path/to/%%23?foo=the%231#bar',
|
||||
pathname: '/path/to/%#',
|
||||
query: {
|
||||
foo: 'the#1'
|
||||
'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'
|
||||
},
|
||||
'http://[::]/': {
|
||||
href: 'http://[::]/',
|
||||
protocol: 'http:',
|
||||
hostname: '[::]',
|
||||
pathname: '/'
|
||||
},
|
||||
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',
|
||||
},
|
||||
// Encode context-specific delimiters in path and query, but do not touch
|
||||
// other non-delimiter chars like `%`.
|
||||
// <https://github.com/nodejs/node-v0.x-archive/issues/4082>
|
||||
|
||||
// `?` 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',
|
||||
},
|
||||
// `#`,`?` 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'
|
||||
},
|
||||
|
||||
// Multiple `#` in search
|
||||
'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag': {
|
||||
href: 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag',
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: 'example.com',
|
||||
hostname: 'example.com',
|
||||
hash: '#frag',
|
||||
search: '?foo=bar#1#2#3&abc=#4##5',
|
||||
query: {},
|
||||
pathname: '/'
|
||||
},
|
||||
// `#`,`?` 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'
|
||||
},
|
||||
|
||||
// More than 255 characters in hostname which exceeds the limit
|
||||
[`http://${'a'.repeat(255)}.com/node`]: {
|
||||
href: 'http:///node',
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: '',
|
||||
hostname: '',
|
||||
pathname: '/node',
|
||||
path: '/node'
|
||||
},
|
||||
// `#` in path end + `#` in query
|
||||
'/path/to/%%23?foo=the%231#bar': {
|
||||
href: '/path/to/%%23?foo=the%231#bar',
|
||||
pathname: '/path/to/%#',
|
||||
query: {
|
||||
foo: 'the#1'
|
||||
},
|
||||
hash: '#bar'
|
||||
},
|
||||
|
||||
// Greater than or equal to 63 characters after `.` in hostname
|
||||
[`http://www.${'z'.repeat(63)}example.com/node`]: {
|
||||
href: `http://www.${'z'.repeat(63)}example.com/node`,
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: `www.${'z'.repeat(63)}example.com`,
|
||||
hostname: `www.${'z'.repeat(63)}example.com`,
|
||||
pathname: '/node',
|
||||
path: '/node'
|
||||
},
|
||||
// `?` 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',
|
||||
},
|
||||
|
||||
// https://github.com/nodejs/node/issues/3361
|
||||
'file:///home/user': {
|
||||
href: 'file:///home/user',
|
||||
protocol: 'file',
|
||||
pathname: '/home/user',
|
||||
path: '/home/user'
|
||||
},
|
||||
// `?` 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',
|
||||
},
|
||||
|
||||
// surrogate in auth
|
||||
'http://%F0%9F%98%80@www.example.com/': {
|
||||
href: 'http://%F0%9F%98%80@www.example.com/',
|
||||
protocol: 'http:',
|
||||
auth: '\uD83D\uDE00',
|
||||
hostname: 'www.example.com',
|
||||
pathname: '/'
|
||||
// Multiple `#` in search
|
||||
'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag': {
|
||||
href: 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag',
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: 'example.com',
|
||||
hostname: 'example.com',
|
||||
hash: '#frag',
|
||||
search: '?foo=bar#1#2#3&abc=#4##5',
|
||||
query: {},
|
||||
pathname: '/'
|
||||
},
|
||||
|
||||
// More than 255 characters in hostname which exceeds the limit
|
||||
[`http://${'a'.repeat(255)}.com/node`]: {
|
||||
href: 'http:///node',
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: '',
|
||||
hostname: '',
|
||||
pathname: '/node',
|
||||
path: '/node'
|
||||
},
|
||||
|
||||
// Greater than or equal to 63 characters after `.` in hostname
|
||||
[`http://www.${'z'.repeat(63)}example.com/node`]: {
|
||||
href: `http://www.${'z'.repeat(63)}example.com/node`,
|
||||
protocol: 'http:',
|
||||
slashes: true,
|
||||
host: `www.${'z'.repeat(63)}example.com`,
|
||||
hostname: `www.${'z'.repeat(63)}example.com`,
|
||||
pathname: '/node',
|
||||
path: '/node'
|
||||
},
|
||||
|
||||
// https://github.com/nodejs/node/issues/3361
|
||||
'file:///home/user': {
|
||||
href: 'file:///home/user',
|
||||
protocol: 'file',
|
||||
pathname: '/home/user',
|
||||
path: '/home/user'
|
||||
},
|
||||
|
||||
// surrogate in auth
|
||||
'http://%F0%9F%98%80@www.example.com/': {
|
||||
href: 'http://%F0%9F%98%80@www.example.com/',
|
||||
protocol: 'http:',
|
||||
auth: '\uD83D\uDE00',
|
||||
hostname: 'www.example.com',
|
||||
pathname: '/'
|
||||
}
|
||||
};
|
||||
|
||||
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.strictEqual(actual, expect,
|
||||
`wonky format(${u}) == ${expect}\nactual:${actual}`);
|
||||
assert.strictEqual(actualObj, expect,
|
||||
`wonky format(${JSON.stringify(formatTests[u])}) == ${
|
||||
expect}\nactual: ${actualObj}`);
|
||||
}
|
||||
};
|
||||
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.strictEqual(actual, expect,
|
||||
`wonky format(${u}) == ${expect}\nactual:${actual}`);
|
||||
assert.strictEqual(actualObj, expect,
|
||||
`wonky format(${JSON.stringify(formatTests[u])}) == ${
|
||||
expect}\nactual: ${actualObj}`);
|
||||
}
|
||||
});
|
||||
|
||||
19
test/js/node/test/parallel/test-url-is-url-internal.js
Normal file
19
test/js/node/test/parallel/test-url-is-url-internal.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// Flags: --expose-internals
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
|
||||
const { URL, parse } = require('node:url');
|
||||
const assert = require('node:assert');
|
||||
const { isURL } = require('internal/url');
|
||||
const { test } = require('node:test');
|
||||
|
||||
test('isURL', () => {
|
||||
assert.strictEqual(isURL(new URL('https://www.nodejs.org')), true);
|
||||
assert.strictEqual(isURL(parse('https://www.nodejs.org')), false);
|
||||
assert.strictEqual(isURL({
|
||||
href: 'https://www.nodejs.org',
|
||||
protocol: 'https:',
|
||||
path: '/',
|
||||
}), false);
|
||||
});
|
||||
@@ -1,13 +1,10 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const { hasIntl } = require('../common');
|
||||
|
||||
if (!common.hasIntl)
|
||||
common.skip('missing Intl');
|
||||
|
||||
const assert = require('assert');
|
||||
const inspect = require('util').inspect;
|
||||
|
||||
const url = require('url');
|
||||
const assert = require('node:assert');
|
||||
const { inspect } = require('node:util');
|
||||
const url = require('node:url');
|
||||
const { test } = require('node:test');
|
||||
|
||||
// URLs to parse, and expected data
|
||||
// { url : parsed }
|
||||
@@ -1025,36 +1022,38 @@ const parseTests = {
|
||||
}
|
||||
};
|
||||
|
||||
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]);
|
||||
test('should parse and format', { skip: !hasIntl }, () => {
|
||||
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;
|
||||
}
|
||||
});
|
||||
Object.keys(actual).forEach(function(i) {
|
||||
if (expected[i] === undefined && actual[i] === null) {
|
||||
expected[i] = null;
|
||||
}
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(
|
||||
actual,
|
||||
expected,
|
||||
`parsing ${u} and expected ${inspect(expected)} but got ${inspect(actual)}`
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
spaced,
|
||||
expected,
|
||||
`expected ${inspect(expected)}, got ${inspect(spaced)}`
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
actual,
|
||||
expected,
|
||||
`parsing ${u} and expected ${inspect(expected)} but got ${inspect(actual)}`
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
spaced,
|
||||
expected,
|
||||
`expected ${inspect(expected)}, got ${inspect(spaced)}`
|
||||
);
|
||||
|
||||
expected = parseTests[u].href;
|
||||
actual = url.format(parseTests[u]);
|
||||
expected = parseTests[u].href;
|
||||
actual = url.format(parseTests[u]);
|
||||
|
||||
assert.strictEqual(actual, expected,
|
||||
`format(${u}) == ${u}\nactual:${actual}`);
|
||||
}
|
||||
assert.strictEqual(actual, expected,
|
||||
`format(${u}) == ${u}\nactual:${actual}`);
|
||||
}
|
||||
});
|
||||
|
||||
{
|
||||
test('parse result should equal new url.Url()', { skip: !hasIntl }, () => {
|
||||
const parsed = url.parse('http://nodejs.org/')
|
||||
.resolveObject('jAvascript:alert(1);a=\x27@white-listed.com\x27');
|
||||
|
||||
@@ -1074,4 +1073,4 @@ for (const u in parseTests) {
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(parsed, expected);
|
||||
}
|
||||
});
|
||||
|
||||
103
test/js/node/test/parallel/test-url-parse-invalid-input.js
Normal file
103
test/js/node/test/parallel/test-url-parse-invalid-input.js
Normal file
@@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const url = require('url');
|
||||
|
||||
// https://github.com/joyent/node/issues/568
|
||||
[
|
||||
[undefined, 'undefined'],
|
||||
[null, 'object'],
|
||||
[true, 'boolean'],
|
||||
[false, 'boolean'],
|
||||
[0.0, 'number'],
|
||||
[0, 'number'],
|
||||
[[], 'object'],
|
||||
[{}, 'object'],
|
||||
[() => {}, 'function'],
|
||||
[Symbol('foo'), 'symbol'],
|
||||
].forEach(([val, type]) => {
|
||||
assert.throws(() => {
|
||||
url.parse(val);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "url" argument must be of type string.' +
|
||||
common.invalidArgTypeHelper(val)
|
||||
});
|
||||
});
|
||||
|
||||
assert.throws(() => { url.parse('http://%E0%A4%A@fail'); },
|
||||
(e) => {
|
||||
// The error should be a URIError.
|
||||
if (!(e instanceof URIError))
|
||||
return false;
|
||||
|
||||
// The error should be from the JS engine and not from Node.js.
|
||||
// JS engine errors do not have the `code` property.
|
||||
return e.code === undefined;
|
||||
});
|
||||
|
||||
assert.throws(() => { url.parse('http://[127.0.0.1\x00c8763]:8000/'); },
|
||||
{ code: 'ERR_INVALID_URL', input: 'http://[127.0.0.1\x00c8763]:8000/' }
|
||||
);
|
||||
|
||||
if (common.hasIntl) {
|
||||
// An array of Unicode code points whose Unicode NFKD contains a "bad
|
||||
// character".
|
||||
const badIDNA = (() => {
|
||||
const BAD_CHARS = '#%/:?@[\\]^|';
|
||||
const out = [];
|
||||
for (let i = 0x80; i < 0x110000; i++) {
|
||||
const cp = String.fromCodePoint(i);
|
||||
for (const badChar of BAD_CHARS) {
|
||||
if (cp.normalize('NFKD').includes(badChar)) {
|
||||
out.push(cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
})();
|
||||
|
||||
// The generation logic above should at a minimum produce these two
|
||||
// characters.
|
||||
assert(badIDNA.includes('℀'));
|
||||
assert(badIDNA.includes('@'));
|
||||
|
||||
for (const badCodePoint of badIDNA) {
|
||||
const badURL = `http://fail${badCodePoint}fail.com/`;
|
||||
assert.throws(() => { url.parse(badURL); },
|
||||
(e) => e.code === 'ERR_INVALID_URL',
|
||||
`parsing ${badURL}`);
|
||||
}
|
||||
|
||||
assert.throws(() => { url.parse('http://\u00AD/bad.com/'); },
|
||||
(e) => e.code === 'ERR_INVALID_URL',
|
||||
'parsing http://\u00AD/bad.com/');
|
||||
}
|
||||
|
||||
{
|
||||
const badURLs = [
|
||||
'https://evil.com:.example.com',
|
||||
'git+ssh://git@github.com:npm/npm',
|
||||
];
|
||||
badURLs.forEach((badURL) => {
|
||||
common.spawnPromisified(process.execPath, ['-e', `url.parse(${JSON.stringify(badURL)})`])
|
||||
.then(common.mustCall(({ code, stdout, stderr }) => {
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.match(stderr, /\[DEP0170\] DeprecationWarning:/);
|
||||
}));
|
||||
});
|
||||
|
||||
// Warning should only happen once per process.
|
||||
common.expectWarning({
|
||||
DeprecationWarning: {
|
||||
// eslint-disable-next-line @stylistic/js/max-len
|
||||
DEP0169: '`url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.',
|
||||
DEP0170: `The URL ${badURLs[0]} is invalid. Future versions of Node.js will throw an error.`,
|
||||
},
|
||||
});
|
||||
badURLs.forEach((badURL) => {
|
||||
url.parse(badURL);
|
||||
});
|
||||
}
|
||||
90
test/js/node/test/parallel/test-url-parse-query.js
Normal file
90
test/js/node/test/parallel/test-url-parse-query.js
Normal file
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const url = require('url');
|
||||
|
||||
function createWithNoPrototype(properties = []) {
|
||||
const noProto = { __proto__: 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]);
|
||||
});
|
||||
}
|
||||
|
||||
const 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: null,
|
||||
pathname: '/',
|
||||
path: '/'
|
||||
},
|
||||
'/example': {
|
||||
protocol: null,
|
||||
slashes: null,
|
||||
auth: undefined,
|
||||
host: null,
|
||||
port: null,
|
||||
hostname: null,
|
||||
hash: null,
|
||||
search: null,
|
||||
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]);
|
||||
}
|
||||
});
|
||||
}
|
||||
225
test/js/node/test/parallel/test-url-pathtofileurl.js
Normal file
225
test/js/node/test/parallel/test-url-pathtofileurl.js
Normal file
@@ -0,0 +1,225 @@
|
||||
'use strict';
|
||||
|
||||
const { isWindows } = require('../common');
|
||||
const assert = require('assert');
|
||||
const url = require('url');
|
||||
|
||||
{
|
||||
const fileURL = url.pathToFileURL('test/').href;
|
||||
assert.ok(fileURL.startsWith('file:///'));
|
||||
assert.ok(fileURL.endsWith('/'));
|
||||
}
|
||||
|
||||
{
|
||||
const fileURL = url.pathToFileURL('test\\').href;
|
||||
assert.ok(fileURL.startsWith('file:///'));
|
||||
assert.match(fileURL, isWindows ? /\/$/ : /%5C$/);
|
||||
}
|
||||
|
||||
{
|
||||
const fileURL = url.pathToFileURL('test/%').href;
|
||||
assert.ok(fileURL.includes('%25'));
|
||||
}
|
||||
|
||||
{
|
||||
if (isWindows) {
|
||||
// UNC path: \\server\share\resource
|
||||
|
||||
// Missing server:
|
||||
assert.throws(() => url.pathToFileURL('\\\\\\no-server'), {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
});
|
||||
|
||||
// Missing share or resource:
|
||||
assert.throws(() => url.pathToFileURL('\\\\host'), {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
});
|
||||
|
||||
// Regression test for direct String.prototype.startsWith call
|
||||
assert.throws(() => url.pathToFileURL([
|
||||
'\\\\',
|
||||
{ [Symbol.toPrimitive]: () => 'blep\\blop' },
|
||||
]), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
assert.throws(() => url.pathToFileURL(['\\\\', 'blep\\blop']), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
assert.throws(() => url.pathToFileURL({
|
||||
[Symbol.toPrimitive]: () => '\\\\blep\\blop',
|
||||
}), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
|
||||
} else {
|
||||
// UNC paths on posix are considered a single path that has backslashes:
|
||||
const fileURL = url.pathToFileURL('\\\\nas\\share\\path.txt').href;
|
||||
assert.match(fileURL, /file:\/\/.+%5C%5Cnas%5Cshare%5Cpath\.txt$/);
|
||||
}
|
||||
}
|
||||
|
||||
const windowsTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: 'C:\\foo', expected: 'file:///C:/foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', expected: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', expected: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', expected: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', expected: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', expected: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', expected: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', expected: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', expected: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', expected: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', expected: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', expected: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', expected: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', expected: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', expected: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', expected: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', expected: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', expected: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', expected: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', expected: 'file:///C:/%F0%9F%9A%80' },
|
||||
// caret
|
||||
{ path: 'C:\\foo^bar', expected: 'file:///C:/foo%5Ebar' },
|
||||
// left bracket
|
||||
{ path: 'C:\\foo[bar', expected: 'file:///C:/foo%5Bbar' },
|
||||
// right bracket
|
||||
{ path: 'C:\\foo]bar', expected: 'file:///C:/foo%5Dbar' },
|
||||
// Local extended path
|
||||
{ path: '\\\\?\\C:\\path\\to\\file.txt', expected: 'file:///C:/path/to/file.txt' },
|
||||
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
|
||||
{ path: '\\\\nas\\My Docs\\File.doc', expected: 'file://nas/My%20Docs/File.doc' },
|
||||
// Extended UNC path
|
||||
{ path: '\\\\?\\UNC\\server\\share\\folder\\file.txt', expected: 'file://server/share/folder/file.txt' },
|
||||
];
|
||||
const alphabet = String.fromCharCode(...Array.from({ length: 26 }, (_, i) => 'a'.charCodeAt() + i));
|
||||
const posixTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: '/foo', expected: 'file:///foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: '/FOO', expected: 'file:///FOO' },
|
||||
// dir
|
||||
{ path: '/dir/foo', expected: 'file:///dir/foo' },
|
||||
// trailing separator
|
||||
{ path: '/dir/', expected: 'file:///dir/' },
|
||||
// dot
|
||||
{ path: '/foo.mjs', expected: 'file:///foo.mjs' },
|
||||
// space
|
||||
{ path: '/foo bar', expected: 'file:///foo%20bar' },
|
||||
// question mark
|
||||
{ path: '/foo?bar', expected: 'file:///foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: '/foo#bar', expected: 'file:///foo%23bar' },
|
||||
// ampersand
|
||||
{ path: '/foo&bar', expected: 'file:///foo&bar' },
|
||||
// equals
|
||||
{ path: '/foo=bar', expected: 'file:///foo=bar' },
|
||||
// colon
|
||||
{ path: '/foo:bar', expected: 'file:///foo:bar' },
|
||||
// semicolon
|
||||
{ path: '/foo;bar', expected: 'file:///foo;bar' },
|
||||
// percent
|
||||
{ path: '/foo%bar', expected: 'file:///foo%25bar' },
|
||||
// backslash
|
||||
{ path: '/foo\\bar', expected: 'file:///foo%5Cbar' },
|
||||
// backspace
|
||||
{ path: '/foo\bbar', expected: 'file:///foo%08bar' },
|
||||
// tab
|
||||
{ path: '/foo\tbar', expected: 'file:///foo%09bar' },
|
||||
// newline
|
||||
{ path: '/foo\nbar', expected: 'file:///foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: '/foo\rbar', expected: 'file:///foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: '/€', expected: 'file:///%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: '/🚀', expected: 'file:///%F0%9F%9A%80' },
|
||||
// "unsafe" chars
|
||||
{ path: '/foo\r\n\t<>"#%{}|^[\\~]`?bar', expected: 'file:///foo%0D%0A%09%3C%3E%22%23%25%7B%7D%7C%5E%5B%5C%7E%5D%60%3Fbar' },
|
||||
// All of the 16-bit UTF-16 chars
|
||||
{
|
||||
path: `/${Array.from({ length: 0x7FFF }, (_, i) => String.fromCharCode(i)).join('')}`,
|
||||
expected: `file:///${
|
||||
Array.from({ length: 0x21 }, (_, i) => `%${i.toString(16).toUpperCase().padStart(2, '0')}`).join('')
|
||||
}!%22%23$%25&'()*+,-./0123456789:;%3C=%3E%3F@${
|
||||
alphabet.toUpperCase()
|
||||
}%5B%5C%5D%5E_%60${alphabet}%7B%7C%7D%7E%7F${
|
||||
Array.from({ length: 0x800 - 0x80 }, (_, i) => `%${
|
||||
(Math.floor((i - 0x80) / 0x40) + 0xC4).toString(16).toUpperCase()
|
||||
}%${
|
||||
((i % 0x40) + 0x80).toString(16).toUpperCase()
|
||||
}`).join('')
|
||||
}${
|
||||
Array.from({ length: 0x7FFF - 0x800 }, (_, i) => i + 0x800).map((i) => `%E${
|
||||
(i >> 12).toString(16).toUpperCase()
|
||||
}%${
|
||||
(((i >> 6) % 0x40) + 0x80).toString(16).toUpperCase()
|
||||
}%${
|
||||
((i % 0x40) + 0x80).toString(16).toUpperCase()
|
||||
}`).join('')
|
||||
}`
|
||||
},
|
||||
// Trying with some pair of 16-bit surrogate pseudo-characters
|
||||
{ path: `/${String.fromCodePoint(0x1F303)}`, expected: 'file:///%F0%9F%8C%83' },
|
||||
];
|
||||
|
||||
for (const { path, expected } of windowsTestCases) {
|
||||
const actual = url.pathToFileURL(path, { windows: true }).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
for (const { path, expected } of posixTestCases) {
|
||||
const actual = url.pathToFileURL(path, { windows: false }).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
const testCases = isWindows ? windowsTestCases : posixTestCases;
|
||||
|
||||
// Test when `options` is null
|
||||
const whenNullActual = url.pathToFileURL(testCases[0].path, null);
|
||||
assert.strictEqual(whenNullActual.href, testCases[0].expected);
|
||||
|
||||
for (const { path, expected } of testCases) {
|
||||
const actual = url.pathToFileURL(path).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
// Test for non-string parameter
|
||||
{
|
||||
for (const badPath of [
|
||||
undefined, null, true, 42, 42n, Symbol('42'), NaN, {}, [], () => {},
|
||||
Promise.resolve('foo'),
|
||||
new Date(),
|
||||
new String('notPrimitive'),
|
||||
{ toString() { return 'amObject'; } },
|
||||
{ [Symbol.toPrimitive]: (hint) => 'amObject' },
|
||||
]) {
|
||||
assert.throws(() => url.pathToFileURL(badPath), {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
}
|
||||
}
|
||||
443
test/js/node/test/parallel/test-url-relative.js
Normal file
443
test/js/node/test/parallel/test-url-relative.js
Normal file
@@ -0,0 +1,443 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const inspect = require('util').inspect;
|
||||
const url = require('url');
|
||||
|
||||
// When source is false
|
||||
assert.strictEqual(url.resolveObject('', 'foo'), 'foo');
|
||||
|
||||
// [from, path, expected]
|
||||
const 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'],
|
||||
['https://registry.npmjs.org', '@foo/bar', 'https://registry.npmjs.org/@foo/bar'],
|
||||
];
|
||||
for (let i = 0; i < relativeTests.length; i++) {
|
||||
const relativeTest = relativeTests[i];
|
||||
|
||||
const a = url.resolve(relativeTest[0], relativeTest[1]);
|
||||
const e = relativeTest[2];
|
||||
assert.strictEqual(a, e,
|
||||
`resolve(${relativeTest[0]}, ${relativeTest[1]})` +
|
||||
` == ${e}\n actual=${a}`);
|
||||
}
|
||||
|
||||
//
|
||||
// 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 <http://cixar.com/~kris.kowal>
|
||||
// used with permission under MIT License
|
||||
//
|
||||
// Changes marked with @isaacs
|
||||
|
||||
const 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]
|
||||
const 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'],
|
||||
|
||||
// changing auth
|
||||
['http://diff:auth@www.example.com',
|
||||
'http://asdf:qwer@www.example.com',
|
||||
'http://diff:auth@www.example.com/'],
|
||||
|
||||
// changing port
|
||||
['https://example.com:81/',
|
||||
'https://example.com:82/',
|
||||
'https://example.com:81/'],
|
||||
|
||||
// https://github.com/nodejs/node/issues/1435
|
||||
['https://another.host.com/',
|
||||
'https://user:password@example.org/',
|
||||
'https://another.host.com/'],
|
||||
['//another.host.com/',
|
||||
'https://user:password@example.org/',
|
||||
'https://another.host.com/'],
|
||||
['http://another.host.com/',
|
||||
'https://user:password@example.org/',
|
||||
'http://another.host.com/'],
|
||||
['mailto:another.host.com',
|
||||
'mailto:user@example.org',
|
||||
'mailto:another.host.com'],
|
||||
['https://example.com/foo',
|
||||
'https://user:password@example.com',
|
||||
'https://user:password@example.com/foo'],
|
||||
|
||||
// No path at all
|
||||
['#hash1', '#hash2', '#hash1'],
|
||||
];
|
||||
for (let i = 0; i < relativeTests2.length; i++) {
|
||||
const relativeTest = relativeTests2[i];
|
||||
|
||||
const a = url.resolve(relativeTest[1], relativeTest[0]);
|
||||
const e = url.format(relativeTest[2]);
|
||||
assert.strictEqual(a, e,
|
||||
`resolve(${relativeTest[0]}, ${relativeTest[1]})` +
|
||||
` == ${e}\n actual=${a}`);
|
||||
}
|
||||
|
||||
// If format and parse are inverse operations then
|
||||
// resolveObject(parse(x), y) == parse(resolve(x, y))
|
||||
|
||||
// format: [from, path, expected]
|
||||
for (let i = 0; i < relativeTests.length; i++) {
|
||||
const relativeTest = relativeTests[i];
|
||||
|
||||
let actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]);
|
||||
let expected = url.parse(relativeTest[2]);
|
||||
|
||||
|
||||
assert.deepStrictEqual(actual, expected);
|
||||
|
||||
expected = relativeTest[2];
|
||||
actual = url.format(actual);
|
||||
|
||||
assert.strictEqual(actual, expected,
|
||||
`format(${actual}) == ${expected}\n` +
|
||||
`actual: ${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);
|
||||
}
|
||||
for (let i = 0; i < relativeTests2.length; i++) {
|
||||
const relativeTest = relativeTests2[i];
|
||||
|
||||
let actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]);
|
||||
let expected = url.parse(relativeTest[2]);
|
||||
|
||||
assert.deepStrictEqual(
|
||||
actual,
|
||||
expected,
|
||||
`expected ${inspect(expected)} but got ${inspect(actual)}`
|
||||
);
|
||||
|
||||
expected = url.format(relativeTest[2]);
|
||||
actual = url.format(actual);
|
||||
|
||||
assert.strictEqual(actual, expected,
|
||||
`format(${relativeTest[1]}) == ${expected}\n` +
|
||||
`actual: ${actual}`);
|
||||
}
|
||||
Reference in New Issue
Block a user