File
Blob: src/workerd/api/node/tests/path-test.js
| 1 | // Copyright (c) 2023 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | import { ok, strictEqual, throws } from 'node:assert'; |
| 5 | |
| 6 | import { default as path } from 'node:path'; |
| 7 | |
| 8 | export const test_path = { |
| 9 | test(ctrl, env, ctx) { |
| 10 | // Test thrown TypeErrors |
| 11 | const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; |
| 12 | |
| 13 | function fail(fn) { |
| 14 | const args = Array.from(arguments).slice(1); |
| 15 | |
| 16 | throws( |
| 17 | () => { |
| 18 | fn.apply(null, args); |
| 19 | }, |
| 20 | { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' } |
| 21 | ); |
| 22 | } |
| 23 | |
| 24 | typeErrorTests.forEach((test) => { |
| 25 | [path.posix].forEach((namespace) => { |
| 26 | fail(namespace.join, test); |
| 27 | fail(namespace.resolve, test); |
| 28 | fail(namespace.normalize, test); |
| 29 | fail(namespace.isAbsolute, test); |
| 30 | fail(namespace.relative, test, 'foo'); |
| 31 | fail(namespace.relative, 'foo', test); |
| 32 | fail(namespace.parse, test); |
| 33 | fail(namespace.dirname, test); |
| 34 | fail(namespace.basename, test); |
| 35 | fail(namespace.extname, test); |
| 36 | |
| 37 | // Undefined is a valid value as the second argument to basename |
| 38 | if (test !== undefined) { |
| 39 | fail(namespace.basename, 'foo', test); |
| 40 | } |
| 41 | }); |
| 42 | }); |
| 43 | |
| 44 | // path.sep tests |
| 45 | // windows |
| 46 | strictEqual(path.win32.sep, '\\'); |
| 47 | // posix |
| 48 | strictEqual(path.posix.sep, '/'); |
| 49 | |
| 50 | // path.delimiter tests |
| 51 | // windows |
| 52 | strictEqual(path.win32.delimiter, ';'); |
| 53 | // posix |
| 54 | strictEqual(path.posix.delimiter, ':'); |
| 55 | |
| 56 | strictEqual(path, path.posix); |
| 57 | }, |
| 58 | }; |
| 59 | |
| 60 | export const test_path_zero_length_strings = { |
| 61 | test(ctrl, env, ctx) { |
| 62 | // Join will internally ignore all the zero-length strings and it will return |
| 63 | // '.' if the joined string is a zero-length string. |
| 64 | strictEqual(path.posix.join(''), '.'); |
| 65 | strictEqual(path.posix.join('', ''), '.'); |
| 66 | strictEqual(path.join('/'), '/'); |
| 67 | strictEqual(path.join('/', ''), '/'); |
| 68 | |
| 69 | // Normalize will return '.' if the input is a zero-length string |
| 70 | strictEqual(path.posix.normalize(''), '.'); |
| 71 | strictEqual(path.normalize('/'), '/'); |
| 72 | |
| 73 | // Since '' is not a valid path in any of the common environments, return false |
| 74 | strictEqual(path.posix.isAbsolute(''), false); |
| 75 | |
| 76 | // Resolve, internally ignores all the zero-length strings and returns the |
| 77 | // current working directory |
| 78 | strictEqual(path.resolve(''), '/'); |
| 79 | strictEqual(path.resolve('', ''), '/'); |
| 80 | |
| 81 | // Relative, internally calls resolve. So, '' is actually the current directory |
| 82 | strictEqual(path.relative('', '/'), ''); |
| 83 | strictEqual(path.relative('/', ''), ''); |
| 84 | strictEqual(path.relative('/', '/'), ''); |
| 85 | }, |
| 86 | }; |
| 87 | |
| 88 | // Ref: https://github.com/nodejs/node/blob/4d6d7d644be4f10f90e5c9c66563736112fffbff/test/parallel/test-path-resolve.js |
| 89 | export const test_path_resolve = { |
| 90 | test(ctrl, env, ctx) { |
| 91 | const failures = []; |
| 92 | const posixyCwd = '/'; |
| 93 | |
| 94 | const resolveTests = [ |
| 95 | [['/var/lib', '../', 'file/'], '/var/file'], |
| 96 | [['/var/lib', '/../', 'file/'], '/file'], |
| 97 | [['a/b/c/', '../../..'], posixyCwd], |
| 98 | [['.'], posixyCwd], |
| 99 | [['/some/dir', '.', '/absolute/'], '/absolute'], |
| 100 | [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'], |
| 101 | ]; |
| 102 | resolveTests.forEach(([test, expected]) => { |
| 103 | const actual = path.resolve.apply(null, test); |
| 104 | const message = `path.posix.resolve(${test.map(JSON.stringify).join(',')})\n expect=${JSON.stringify( |
| 105 | expected |
| 106 | )}\n actual=${JSON.stringify(actual)}`; |
| 107 | if (actual !== expected) failures.push(message); |
| 108 | }); |
| 109 | strictEqual(failures.length, 0, failures.join('\n')); |
| 110 | }, |
| 111 | }; |
| 112 | |
| 113 | // Ref: https://github.com/nodejs/node/blob/4d6d7d644be4f10f90e5c9c66563736112fffbff/test/parallel/test-path-relative.js |
| 114 | export const test_path_relative = { |
| 115 | test(ctrl, env, ctx) { |
| 116 | const failures = []; |
| 117 | |
| 118 | const relativeTests = [ |
| 119 | ['/var/lib', '/var', '..'], |
| 120 | ['/var/lib', '/bin', '../../bin'], |
| 121 | ['/var/lib', '/var/lib', ''], |
| 122 | ['/var/lib', '/var/apache', '../apache'], |
| 123 | ['/var/', '/var/lib', 'lib'], |
| 124 | ['/', '/var/lib', 'var/lib'], |
| 125 | ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'], |
| 126 | ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'], |
| 127 | ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'], |
| 128 | ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'], |
| 129 | ['/baz-quux', '/baz', '../baz'], |
| 130 | ['/baz', '/baz-quux', '../baz-quux'], |
| 131 | ['/page1/page2/foo', '/', '../../..'], |
| 132 | ]; |
| 133 | relativeTests.forEach((test) => { |
| 134 | const actual = path.relative(test[0], test[1]); |
| 135 | const expected = test[2]; |
| 136 | if (actual !== expected) { |
| 137 | const message = `path.posix.relative(${test |
| 138 | .slice(0, 2) |
| 139 | .map(JSON.stringify) |
| 140 | .join(',')})\n expect=${JSON.stringify( |
| 141 | expected |
| 142 | )}\n actual=${JSON.stringify(actual)}`; |
| 143 | failures.push(`\n${message}`); |
| 144 | } |
| 145 | }); |
| 146 | strictEqual(failures.length, 0, failures.join('')); |
| 147 | }, |
| 148 | }; |
| 149 | |
| 150 | export const test_path_parse_format = { |
| 151 | test(ctrl, env, ctx) { |
| 152 | const unixPaths = [ |
| 153 | // [path, root] |
| 154 | ['/home/user/dir/file.txt', '/'], |
| 155 | ['/home/user/a dir/another File.zip', '/'], |
| 156 | ['/home/user/a dir//another&File.', '/'], |
| 157 | ['/home/user/a$$$dir//another File.zip', '/'], |
| 158 | ['user/dir/another File.zip', ''], |
| 159 | ['file', ''], |
| 160 | ['.\\file', ''], |
| 161 | ['./file', ''], |
| 162 | ['C:\\foo', ''], |
| 163 | ['/', '/'], |
| 164 | ['', ''], |
| 165 | ['.', ''], |
| 166 | ['..', ''], |
| 167 | ['/foo', '/'], |
| 168 | ['/foo.', '/'], |
| 169 | ['/foo.bar', '/'], |
| 170 | ['/.', '/'], |
| 171 | ['/.foo', '/'], |
| 172 | ['/.foo.bar', '/'], |
| 173 | ['/foo/bar.baz', '/'], |
| 174 | ]; |
| 175 | |
| 176 | const unixSpecialCaseFormatTests = [ |
| 177 | [{ dir: 'some/dir' }, 'some/dir/'], |
| 178 | [{ base: 'index.html' }, 'index.html'], |
| 179 | [{ root: '/' }, '/'], |
| 180 | [{ name: 'index', ext: '.html' }, 'index.html'], |
| 181 | [{ dir: 'some/dir', name: 'index', ext: '.html' }, 'some/dir/index.html'], |
| 182 | [{ root: '/', name: 'index', ext: '.html' }, '/index.html'], |
| 183 | [{}, ''], |
| 184 | ]; |
| 185 | |
| 186 | const errors = [ |
| 187 | { method: 'parse', input: [null] }, |
| 188 | { method: 'parse', input: [{}] }, |
| 189 | { method: 'parse', input: [true] }, |
| 190 | { method: 'parse', input: [1] }, |
| 191 | { method: 'parse', input: [] }, |
| 192 | { method: 'format', input: [null] }, |
| 193 | { method: 'format', input: [''] }, |
| 194 | { method: 'format', input: [true] }, |
| 195 | { method: 'format', input: [1] }, |
| 196 | ]; |
| 197 | |
| 198 | checkParseFormat(unixPaths); |
| 199 | checkErrors(); |
| 200 | checkFormat(unixSpecialCaseFormatTests); |
| 201 | |
| 202 | // Test removal of trailing path separators |
| 203 | const trailingTests = [ |
| 204 | ['./', { root: '', dir: '', base: '.', ext: '', name: '.' }], |
| 205 | ['//', { root: '/', dir: '/', base: '', ext: '', name: '' }], |
| 206 | ['///', { root: '/', dir: '/', base: '', ext: '', name: '' }], |
| 207 | ['/foo///', { root: '/', dir: '/', base: 'foo', ext: '', name: 'foo' }], |
| 208 | [ |
| 209 | '/foo///bar.baz', |
| 210 | { root: '/', dir: '/foo//', base: 'bar.baz', ext: '.baz', name: 'bar' }, |
| 211 | ], |
| 212 | ]; |
| 213 | const failures = []; |
| 214 | trailingTests.forEach((test) => { |
| 215 | const actual = path.parse(test[0]); |
| 216 | const expected = test[1]; |
| 217 | const message = `path.posix.parse(${JSON.stringify(test[0])})\n expect=${JSON.stringify( |
| 218 | expected |
| 219 | )}\n actual=${JSON.stringify(actual)}`; |
| 220 | const actualKeys = Object.keys(actual); |
| 221 | const expectedKeys = Object.keys(expected); |
| 222 | let failed = actualKeys.length !== expectedKeys.length; |
| 223 | if (!failed) { |
| 224 | for (let i = 0; i < actualKeys.length; ++i) { |
| 225 | const key = actualKeys[i]; |
| 226 | if (!expectedKeys.includes(key) || actual[key] !== expected[key]) { |
| 227 | failed = true; |
| 228 | break; |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | if (failed) failures.push(`\n${message}`); |
| 233 | }); |
| 234 | strictEqual(failures.length, 0, failures.join('')); |
| 235 | |
| 236 | function checkErrors() { |
| 237 | errors.forEach(({ method, input }) => { |
| 238 | throws( |
| 239 | () => { |
| 240 | path[method].apply(path, input); |
| 241 | }, |
| 242 | { |
| 243 | code: 'ERR_INVALID_ARG_TYPE', |
| 244 | name: 'TypeError', |
| 245 | } |
| 246 | ); |
| 247 | }); |
| 248 | } |
| 249 | |
| 250 | function checkParseFormat(paths) { |
| 251 | paths.forEach(([element, root]) => { |
| 252 | const output = path.parse(element); |
| 253 | strictEqual(typeof output.root, 'string'); |
| 254 | strictEqual(typeof output.dir, 'string'); |
| 255 | strictEqual(typeof output.base, 'string'); |
| 256 | strictEqual(typeof output.ext, 'string'); |
| 257 | strictEqual(typeof output.name, 'string'); |
| 258 | strictEqual(path.format(output), element); |
| 259 | strictEqual(output.root, root); |
| 260 | ok(output.dir.startsWith(output.root)); |
| 261 | strictEqual(output.dir, output.dir ? path.dirname(element) : ''); |
| 262 | strictEqual(output.base, path.basename(element)); |
| 263 | strictEqual(output.ext, path.extname(element)); |
| 264 | }); |
| 265 | } |
| 266 | |
| 267 | function checkFormat(testCases) { |
| 268 | testCases.forEach(([element, expect]) => { |
| 269 | strictEqual(path.format(element), expect); |
| 270 | }); |
| 271 | |
| 272 | [null, undefined, 1, true, false, 'string'].forEach((pathObject) => { |
| 273 | throws( |
| 274 | () => { |
| 275 | path.format(pathObject); |
| 276 | }, |
| 277 | { |
| 278 | code: 'ERR_INVALID_ARG_TYPE', |
| 279 | name: 'TypeError', |
| 280 | } |
| 281 | ); |
| 282 | }); |
| 283 | } |
| 284 | |
| 285 | // See https://github.com/nodejs/node/issues/44343 |
| 286 | strictEqual(path.format({ name: 'x', ext: 'png' }), 'x.png'); |
| 287 | strictEqual(path.format({ name: 'x', ext: '.png' }), 'x.png'); |
| 288 | }, |
| 289 | }; |
| 290 | |
| 291 | export const test_path_normalize = { |
| 292 | test(ctrl, env, ctx) { |
| 293 | strictEqual( |
| 294 | path.win32.normalize('./fixtures///b/../b/c.js'), |
| 295 | 'fixtures\\b\\c.js' |
| 296 | ); |
| 297 | strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar'); |
| 298 | strictEqual(path.win32.normalize('a//b//../b'), 'a\\b'); |
| 299 | strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c'); |
| 300 | strictEqual(path.win32.normalize('a//b//.'), 'a\\b'); |
| 301 | strictEqual( |
| 302 | path.win32.normalize('//server/share/dir/file.ext'), |
| 303 | '\\\\server\\share\\dir\\file.ext' |
| 304 | ); |
| 305 | strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z'); |
| 306 | strictEqual(path.win32.normalize('C:'), 'C:.'); |
| 307 | strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc'); |
| 308 | strictEqual( |
| 309 | path.win32.normalize('C:..\\..\\abc\\..\\def'), |
| 310 | 'C:..\\..\\def' |
| 311 | ); |
| 312 | strictEqual(path.win32.normalize('C:\\.'), 'C:\\'); |
| 313 | strictEqual(path.win32.normalize('file:stream'), 'file:stream'); |
| 314 | strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\'); |
| 315 | strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar'); |
| 316 | strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); |
| 317 | strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); |
| 318 | strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); |
| 319 | strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'), '..\\..\\bar'); |
| 320 | strictEqual( |
| 321 | path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'), |
| 322 | '..\\..\\bar' |
| 323 | ); |
| 324 | strictEqual( |
| 325 | path.win32.normalize('../../../foo/../../../bar'), |
| 326 | '..\\..\\..\\..\\..\\bar' |
| 327 | ); |
| 328 | strictEqual( |
| 329 | path.win32.normalize('../../../foo/../../../bar/../../'), |
| 330 | '..\\..\\..\\..\\..\\..\\' |
| 331 | ); |
| 332 | strictEqual( |
| 333 | path.win32.normalize('../foobar/barfoo/foo/../../../bar/../../'), |
| 334 | '..\\..\\' |
| 335 | ); |
| 336 | strictEqual( |
| 337 | path.win32.normalize('../.../../foobar/../../../bar/../../baz'), |
| 338 | '..\\..\\..\\..\\baz' |
| 339 | ); |
| 340 | strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz'); |
| 341 | |
| 342 | strictEqual( |
| 343 | path.posix.normalize('./fixtures///b/../b/c.js'), |
| 344 | 'fixtures/b/c.js' |
| 345 | ); |
| 346 | strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar'); |
| 347 | strictEqual(path.posix.normalize('a//b//../b'), 'a/b'); |
| 348 | strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c'); |
| 349 | strictEqual(path.posix.normalize('a//b//.'), 'a/b'); |
| 350 | strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z'); |
| 351 | strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar'); |
| 352 | strictEqual(path.posix.normalize('bar/foo../../'), 'bar/'); |
| 353 | strictEqual(path.posix.normalize('bar/foo../..'), 'bar'); |
| 354 | strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); |
| 355 | strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); |
| 356 | strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); |
| 357 | strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar'); |
| 358 | strictEqual(path.posix.normalize('../.../.././.../../../bar'), '../../bar'); |
| 359 | strictEqual( |
| 360 | path.posix.normalize('../../../foo/../../../bar'), |
| 361 | '../../../../../bar' |
| 362 | ); |
| 363 | strictEqual( |
| 364 | path.posix.normalize('../../../foo/../../../bar/../../'), |
| 365 | '../../../../../../' |
| 366 | ); |
| 367 | strictEqual( |
| 368 | path.posix.normalize('../foobar/barfoo/foo/../../../bar/../../'), |
| 369 | '../../' |
| 370 | ); |
| 371 | strictEqual( |
| 372 | path.posix.normalize('../.../../foobar/../../../bar/../../baz'), |
| 373 | '../../../../baz' |
| 374 | ); |
| 375 | strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz'); |
| 376 | }, |
| 377 | }; |
| 378 | |
| 379 | export const test_path_join = { |
| 380 | test(ctrl, env, ctx) { |
| 381 | const failures = []; |
| 382 | const _backslashRE = /\\/g; |
| 383 | |
| 384 | const joinTests = [ |
| 385 | [['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], |
| 386 | [[], '.'], |
| 387 | [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], |
| 388 | [['/foo', '../../../bar'], '/bar'], |
| 389 | [['foo', '../../../bar'], '../../bar'], |
| 390 | [['foo/', '../../../bar'], '../../bar'], |
| 391 | [['foo/x', '../../../bar'], '../bar'], |
| 392 | [['foo/x', './bar'], 'foo/x/bar'], |
| 393 | [['foo/x/', './bar'], 'foo/x/bar'], |
| 394 | [['foo/x/', '.', 'bar'], 'foo/x/bar'], |
| 395 | [['./'], './'], |
| 396 | [['.', './'], './'], |
| 397 | [['.', '.', '.'], '.'], |
| 398 | [['.', './', '.'], '.'], |
| 399 | [['.', '/./', '.'], '.'], |
| 400 | [['.', '/////./', '.'], '.'], |
| 401 | [['.'], '.'], |
| 402 | [['', '.'], '.'], |
| 403 | [['', 'foo'], 'foo'], |
| 404 | [['foo', '/bar'], 'foo/bar'], |
| 405 | [['', '/foo'], '/foo'], |
| 406 | [['', '', '/foo'], '/foo'], |
| 407 | [['', '', 'foo'], 'foo'], |
| 408 | [['foo', ''], 'foo'], |
| 409 | [['foo/', ''], 'foo/'], |
| 410 | [['foo', '', '/bar'], 'foo/bar'], |
| 411 | [['./', '..', '/foo'], '../foo'], |
| 412 | [['./', '..', '..', '/foo'], '../../foo'], |
| 413 | [['.', '..', '..', '/foo'], '../../foo'], |
| 414 | [['', '..', '..', '/foo'], '../../foo'], |
| 415 | [['/'], '/'], |
| 416 | [['/', '.'], '/'], |
| 417 | [['/', '..'], '/'], |
| 418 | [['/', '..', '..'], '/'], |
| 419 | [[''], '.'], |
| 420 | [['', ''], '.'], |
| 421 | [[' /foo'], ' /foo'], |
| 422 | [[' ', 'foo'], ' /foo'], |
| 423 | [[' ', '.'], ' '], |
| 424 | [[' ', '/'], ' /'], |
| 425 | [[' ', ''], ' '], |
| 426 | [['/', 'foo'], '/foo'], |
| 427 | [['/', '/foo'], '/foo'], |
| 428 | [['/', '//foo'], '/foo'], |
| 429 | [['/', '', '/foo'], '/foo'], |
| 430 | [['', '/', 'foo'], '/foo'], |
| 431 | [['', '/', '/foo'], '/foo'], |
| 432 | ]; |
| 433 | |
| 434 | joinTests.forEach((test) => { |
| 435 | const actual = path.join.apply(null, test[0]); |
| 436 | const expected = test[1]; |
| 437 | // eslint-disable-next-line no-undef |
| 438 | if (actual !== expected && actualAlt !== expected) { |
| 439 | const delimiter = test[0].map(JSON.stringify).join(','); |
| 440 | const message = `path.posix.join(${delimiter})\n expect=${JSON.stringify( |
| 441 | expected |
| 442 | )}\n actual=${JSON.stringify(actual)}`; |
| 443 | failures.push(`\n${message}`); |
| 444 | } |
| 445 | }); |
| 446 | strictEqual(failures.length, 0, failures.join('')); |
| 447 | }, |
| 448 | }; |
| 449 | |
| 450 | export const test_path_isabsolute = { |
| 451 | test(ctrl, env, ctx) { |
| 452 | strictEqual(path.posix.isAbsolute('/home/foo'), true); |
| 453 | strictEqual(path.posix.isAbsolute('/home/foo/..'), true); |
| 454 | strictEqual(path.posix.isAbsolute('bar/'), false); |
| 455 | strictEqual(path.posix.isAbsolute('./baz'), false); |
| 456 | strictEqual(path.isAbsolute('/home/foo'), true); |
| 457 | strictEqual(path.isAbsolute('/home/foo/..'), true); |
| 458 | strictEqual(path.isAbsolute('bar/'), false); |
| 459 | strictEqual(path.isAbsolute('./baz'), false); |
| 460 | }, |
| 461 | }; |
| 462 | |
| 463 | export const test_path_extname = { |
| 464 | test(ctrl, env, ctx) { |
| 465 | const failures = []; |
| 466 | const _slashRE = /\//g; |
| 467 | |
| 468 | [ |
| 469 | ['', ''], |
| 470 | ['/path/to/file', ''], |
| 471 | ['/path/to/file.ext', '.ext'], |
| 472 | ['/path.to/file.ext', '.ext'], |
| 473 | ['/path.to/file', ''], |
| 474 | ['/path.to/.file', ''], |
| 475 | ['/path.to/.file.ext', '.ext'], |
| 476 | ['/path/to/f.ext', '.ext'], |
| 477 | ['/path/to/..ext', '.ext'], |
| 478 | ['/path/to/..', ''], |
| 479 | ['file', ''], |
| 480 | ['file.ext', '.ext'], |
| 481 | ['.file', ''], |
| 482 | ['.file.ext', '.ext'], |
| 483 | ['/file', ''], |
| 484 | ['/file.ext', '.ext'], |
| 485 | ['/.file', ''], |
| 486 | ['/.file.ext', '.ext'], |
| 487 | ['.path/file.ext', '.ext'], |
| 488 | ['file.ext.ext', '.ext'], |
| 489 | ['file.', '.'], |
| 490 | ['.', ''], |
| 491 | ['./', ''], |
| 492 | ['.file.ext', '.ext'], |
| 493 | ['.file', ''], |
| 494 | ['.file.', '.'], |
| 495 | ['.file..', '.'], |
| 496 | ['..', ''], |
| 497 | ['../', ''], |
| 498 | ['..file.ext', '.ext'], |
| 499 | ['..file', '.file'], |
| 500 | ['..file.', '.'], |
| 501 | ['..file..', '.'], |
| 502 | ['...', '.'], |
| 503 | ['...ext', '.ext'], |
| 504 | ['....', '.'], |
| 505 | ['file.ext/', '.ext'], |
| 506 | ['file.ext//', '.ext'], |
| 507 | ['file/', ''], |
| 508 | ['file//', ''], |
| 509 | ['file./', '.'], |
| 510 | ['file.//', '.'], |
| 511 | ].forEach((test) => { |
| 512 | const expected = test[1]; |
| 513 | const actual = path.extname(test[0]); |
| 514 | const message = `path.posix.extname(${JSON.stringify(test[0])})\n expect=${JSON.stringify( |
| 515 | expected |
| 516 | )}\n actual=${JSON.stringify(actual)}`; |
| 517 | if (actual !== expected) failures.push(`\n${message}`); |
| 518 | }); |
| 519 | |
| 520 | strictEqual(failures.length, 0, failures.join('')); |
| 521 | |
| 522 | // On *nix, backslash is a valid name component like any other character. |
| 523 | strictEqual(path.posix.extname('.\\'), ''); |
| 524 | strictEqual(path.posix.extname('..\\'), '.\\'); |
| 525 | strictEqual(path.posix.extname('file.ext\\'), '.ext\\'); |
| 526 | strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\'); |
| 527 | strictEqual(path.posix.extname('file\\'), ''); |
| 528 | strictEqual(path.posix.extname('file\\\\'), ''); |
| 529 | strictEqual(path.posix.extname('file.\\'), '.\\'); |
| 530 | strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); |
| 531 | }, |
| 532 | }; |
| 533 | |
| 534 | export const test_path_dirname = { |
| 535 | test(ctrl, env, ctx) { |
| 536 | strictEqual(path.posix.dirname('/a/b/'), '/a'); |
| 537 | strictEqual(path.posix.dirname('/a/b'), '/a'); |
| 538 | strictEqual(path.posix.dirname('/a'), '/'); |
| 539 | strictEqual(path.posix.dirname(''), '.'); |
| 540 | strictEqual(path.posix.dirname('/'), '/'); |
| 541 | strictEqual(path.posix.dirname('////'), '/'); |
| 542 | strictEqual(path.posix.dirname('//a'), '//'); |
| 543 | strictEqual(path.posix.dirname('foo'), '.'); |
| 544 | strictEqual(path.dirname('/a/b/'), '/a'); |
| 545 | strictEqual(path.dirname('/a/b'), '/a'); |
| 546 | strictEqual(path.dirname('/a'), '/'); |
| 547 | strictEqual(path.dirname(''), '.'); |
| 548 | strictEqual(path.dirname('/'), '/'); |
| 549 | strictEqual(path.dirname('////'), '/'); |
| 550 | strictEqual(path.dirname('//a'), '//'); |
| 551 | strictEqual(path.dirname('foo'), '.'); |
| 552 | }, |
| 553 | }; |
| 554 | |
| 555 | export const test_path_basename = { |
| 556 | test(ctrl, env, ctx) { |
| 557 | strictEqual(path.basename('.js', '.js'), ''); |
| 558 | strictEqual(path.basename('js', '.js'), 'js'); |
| 559 | strictEqual(path.basename('file.js', '.ts'), 'file.js'); |
| 560 | strictEqual(path.basename('file', '.js'), 'file'); |
| 561 | strictEqual(path.basename('file.js.old', '.js.old'), 'file'); |
| 562 | strictEqual(path.basename(''), ''); |
| 563 | strictEqual(path.basename('/dir/basename.ext'), 'basename.ext'); |
| 564 | strictEqual(path.basename('/basename.ext'), 'basename.ext'); |
| 565 | strictEqual(path.basename('basename.ext'), 'basename.ext'); |
| 566 | strictEqual(path.basename('basename.ext/'), 'basename.ext'); |
| 567 | strictEqual(path.basename('basename.ext//'), 'basename.ext'); |
| 568 | strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb'); |
| 569 | strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb'); |
| 570 | strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb'); |
| 571 | strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb'); |
| 572 | strictEqual(path.basename('aaa/bbb', 'bb'), 'b'); |
| 573 | strictEqual(path.basename('aaa/bbb', 'b'), 'bb'); |
| 574 | strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb'); |
| 575 | strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb'); |
| 576 | strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb'); |
| 577 | strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb'); |
| 578 | strictEqual(path.basename('/aaa/bbb', 'bb'), 'b'); |
| 579 | strictEqual(path.basename('/aaa/bbb', 'b'), 'bb'); |
| 580 | strictEqual(path.basename('/aaa/bbb'), 'bbb'); |
| 581 | strictEqual(path.basename('/aaa/'), 'aaa'); |
| 582 | strictEqual(path.basename('/aaa/b'), 'b'); |
| 583 | strictEqual(path.basename('/a/b'), 'b'); |
| 584 | strictEqual(path.basename('//a'), 'a'); |
| 585 | strictEqual(path.basename('a', 'a'), ''); |
| 586 | |
| 587 | // On unix a backslash is just treated as any other character. |
| 588 | strictEqual( |
| 589 | path.posix.basename('\\dir\\basename.ext'), |
| 590 | '\\dir\\basename.ext' |
| 591 | ); |
| 592 | strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext'); |
| 593 | strictEqual(path.posix.basename('basename.ext'), 'basename.ext'); |
| 594 | strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); |
| 595 | strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); |
| 596 | strictEqual(path.posix.basename('foo'), 'foo'); |
| 597 | |
| 598 | // POSIX filenames may include control characters |
| 599 | // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html |
| 600 | const controlCharFilename = `Icon${String.fromCharCode(13)}`; |
| 601 | strictEqual( |
| 602 | path.posix.basename(`/a/b/${controlCharFilename}`), |
| 603 | controlCharFilename |
| 604 | ); |
| 605 | }, |
| 606 | }; |
| 607 | |
| 608 | export const make_sure_posix_win32_works = { |
| 609 | async test() { |
| 610 | { |
| 611 | const pathModule = await import('node:path/win32'); |
| 612 | strictEqual(typeof pathModule.default.resolve, 'function'); |
| 613 | strictEqual( |
| 614 | typeof process.getBuiltinModule('node:path/win32').resolve, |
| 615 | 'function' |
| 616 | ); |
| 617 | } |
| 618 | { |
| 619 | const pathModule = await import('node:path/posix'); |
| 620 | strictEqual(typeof pathModule.default.resolve, 'function'); |
| 621 | strictEqual( |
| 622 | typeof process.getBuiltinModule('node:path/posix').resolve, |
| 623 | 'function' |
| 624 | ); |
| 625 | } |
| 626 | }, |
| 627 | }; |