1'use strict'; 2const { isWindows } = require('../common'); 3const assert = require('assert'); 4const url = require('url'); 5 6{ 7 const fileURL = url.pathToFileURL('test/').href; 8 assert.ok(fileURL.startsWith('file:///')); 9 assert.ok(fileURL.endsWith('/')); 10} 11 12{ 13 const fileURL = url.pathToFileURL('test\\').href; 14 assert.ok(fileURL.startsWith('file:///')); 15 if (isWindows) 16 assert.ok(fileURL.endsWith('/')); 17 else 18 assert.ok(fileURL.endsWith('%5C')); 19} 20 21{ 22 const fileURL = url.pathToFileURL('test/%').href; 23 assert.ok(fileURL.includes('%25')); 24} 25 26{ 27 if (isWindows) { 28 // UNC path: \\server\share\resource 29 30 // Missing server: 31 assert.throws(() => url.pathToFileURL('\\\\\\no-server'), { 32 code: 'ERR_INVALID_ARG_VALUE', 33 }); 34 35 // Missing share or resource: 36 assert.throws(() => url.pathToFileURL('\\\\host'), { 37 code: 'ERR_INVALID_ARG_VALUE', 38 }); 39 40 // Regression test for direct String.prototype.startsWith call 41 assert.throws(() => url.pathToFileURL([ 42 '\\\\', 43 { [Symbol.toPrimitive]: () => 'blep\\blop' }, 44 ]), { 45 code: 'ERR_INVALID_ARG_TYPE', 46 }); 47 assert.throws(() => url.pathToFileURL(['\\\\', 'blep\\blop']), { 48 code: 'ERR_INVALID_ARG_TYPE', 49 }); 50 assert.throws(() => url.pathToFileURL({ 51 [Symbol.toPrimitive]: () => '\\\\blep\\blop', 52 }), { 53 code: 'ERR_INVALID_ARG_TYPE', 54 }); 55 56 } else { 57 // UNC paths on posix are considered a single path that has backslashes: 58 const fileURL = url.pathToFileURL('\\\\nas\\share\\path.txt').href; 59 assert.match(fileURL, /file:\/\/.+%5C%5Cnas%5Cshare%5Cpath\.txt$/); 60 } 61} 62 63{ 64 let testCases; 65 if (isWindows) { 66 testCases = [ 67 // Lowercase ascii alpha 68 { path: 'C:\\foo', expected: 'file:///C:/foo' }, 69 // Uppercase ascii alpha 70 { path: 'C:\\FOO', expected: 'file:///C:/FOO' }, 71 // dir 72 { path: 'C:\\dir\\foo', expected: 'file:///C:/dir/foo' }, 73 // trailing separator 74 { path: 'C:\\dir\\', expected: 'file:///C:/dir/' }, 75 // dot 76 { path: 'C:\\foo.mjs', expected: 'file:///C:/foo.mjs' }, 77 // space 78 { path: 'C:\\foo bar', expected: 'file:///C:/foo%20bar' }, 79 // question mark 80 { path: 'C:\\foo?bar', expected: 'file:///C:/foo%3Fbar' }, 81 // number sign 82 { path: 'C:\\foo#bar', expected: 'file:///C:/foo%23bar' }, 83 // ampersand 84 { path: 'C:\\foo&bar', expected: 'file:///C:/foo&bar' }, 85 // equals 86 { path: 'C:\\foo=bar', expected: 'file:///C:/foo=bar' }, 87 // colon 88 { path: 'C:\\foo:bar', expected: 'file:///C:/foo:bar' }, 89 // semicolon 90 { path: 'C:\\foo;bar', expected: 'file:///C:/foo;bar' }, 91 // percent 92 { path: 'C:\\foo%bar', expected: 'file:///C:/foo%25bar' }, 93 // backslash 94 { path: 'C:\\foo\\bar', expected: 'file:///C:/foo/bar' }, 95 // backspace 96 { path: 'C:\\foo\bbar', expected: 'file:///C:/foo%08bar' }, 97 // tab 98 { path: 'C:\\foo\tbar', expected: 'file:///C:/foo%09bar' }, 99 // newline 100 { path: 'C:\\foo\nbar', expected: 'file:///C:/foo%0Abar' }, 101 // carriage return 102 { path: 'C:\\foo\rbar', expected: 'file:///C:/foo%0Dbar' }, 103 // latin1 104 { path: 'C:\\fóóbàr', expected: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' }, 105 // Euro sign (BMP code point) 106 { path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' }, 107 // Rocket emoji (non-BMP code point) 108 { path: 'C:\\', expected: 'file:///C:/%F0%9F%9A%80' }, 109 // UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows) 110 { path: '\\\\nas\\My Docs\\File.doc', expected: 'file://nas/My%20Docs/File.doc' }, 111 ]; 112 } else { 113 testCases = [ 114 // Lowercase ascii alpha 115 { path: '/foo', expected: 'file:///foo' }, 116 // Uppercase ascii alpha 117 { path: '/FOO', expected: 'file:///FOO' }, 118 // dir 119 { path: '/dir/foo', expected: 'file:///dir/foo' }, 120 // trailing separator 121 { path: '/dir/', expected: 'file:///dir/' }, 122 // dot 123 { path: '/foo.mjs', expected: 'file:///foo.mjs' }, 124 // space 125 { path: '/foo bar', expected: 'file:///foo%20bar' }, 126 // question mark 127 { path: '/foo?bar', expected: 'file:///foo%3Fbar' }, 128 // number sign 129 { path: '/foo#bar', expected: 'file:///foo%23bar' }, 130 // ampersand 131 { path: '/foo&bar', expected: 'file:///foo&bar' }, 132 // equals 133 { path: '/foo=bar', expected: 'file:///foo=bar' }, 134 // colon 135 { path: '/foo:bar', expected: 'file:///foo:bar' }, 136 // semicolon 137 { path: '/foo;bar', expected: 'file:///foo;bar' }, 138 // percent 139 { path: '/foo%bar', expected: 'file:///foo%25bar' }, 140 // backslash 141 { path: '/foo\\bar', expected: 'file:///foo%5Cbar' }, 142 // backspace 143 { path: '/foo\bbar', expected: 'file:///foo%08bar' }, 144 // tab 145 { path: '/foo\tbar', expected: 'file:///foo%09bar' }, 146 // newline 147 { path: '/foo\nbar', expected: 'file:///foo%0Abar' }, 148 // carriage return 149 { path: '/foo\rbar', expected: 'file:///foo%0Dbar' }, 150 // latin1 151 { path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' }, 152 // Euro sign (BMP code point) 153 { path: '/€', expected: 'file:///%E2%82%AC' }, 154 // Rocket emoji (non-BMP code point) 155 { path: '/', expected: 'file:///%F0%9F%9A%80' }, 156 ]; 157 } 158 159 for (const { path, expected } of testCases) { 160 const actual = url.pathToFileURL(path).href; 161 assert.strictEqual(actual, expected); 162 } 163} 164 165// Test for non-string parameter 166{ 167 for (const badPath of [ 168 undefined, null, true, 42, 42n, Symbol('42'), NaN, {}, [], () => {}, 169 Promise.resolve('foo'), 170 new Date(), 171 new String('notPrimitive'), 172 { toString() { return 'amObject'; } }, 173 { [Symbol.toPrimitive]: (hint) => 'amObject' }, 174 ]) { 175 assert.throws(() => url.pathToFileURL(badPath), { 176 code: 'ERR_INVALID_ARG_TYPE', 177 }); 178 } 179} 180