1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24const assert = require('assert'); 25const path = require('path'); 26 27const winPaths = [ 28 // [path, root] 29 ['C:\\path\\dir\\index.html', 'C:\\'], 30 ['C:\\another_path\\DIR\\1\\2\\33\\\\index', 'C:\\'], 31 ['another_path\\DIR with spaces\\1\\2\\33\\index', ''], 32 ['\\', '\\'], 33 ['\\foo\\C:', '\\'], 34 ['file', ''], 35 ['file:stream', ''], 36 ['.\\file', ''], 37 ['C:', 'C:'], 38 ['C:.', 'C:'], 39 ['C:..', 'C:'], 40 ['C:abc', 'C:'], 41 ['C:\\', 'C:\\'], 42 ['C:\\abc', 'C:\\' ], 43 ['', ''], 44 45 // unc 46 ['\\\\server\\share\\file_path', '\\\\server\\share\\'], 47 ['\\\\server two\\shared folder\\file path.zip', 48 '\\\\server two\\shared folder\\'], 49 ['\\\\teela\\admin$\\system32', '\\\\teela\\admin$\\'], 50 ['\\\\?\\UNC\\server\\share', '\\\\?\\UNC\\'], 51]; 52 53const winSpecialCaseParseTests = [ 54 ['t', { base: 't', name: 't', root: '', dir: '', ext: '' }], 55 ['/foo/bar', { root: '/', dir: '/foo', base: 'bar', ext: '', name: 'bar' }], 56]; 57 58const winSpecialCaseFormatTests = [ 59 [{ dir: 'some\\dir' }, 'some\\dir\\'], 60 [{ base: 'index.html' }, 'index.html'], 61 [{ root: 'C:\\' }, 'C:\\'], 62 [{ name: 'index', ext: '.html' }, 'index.html'], 63 [{ dir: 'some\\dir', name: 'index', ext: '.html' }, 'some\\dir\\index.html'], 64 [{ root: 'C:\\', name: 'index', ext: '.html' }, 'C:\\index.html'], 65 [{}, ''], 66]; 67 68const unixPaths = [ 69 // [path, root] 70 ['/home/user/dir/file.txt', '/'], 71 ['/home/user/a dir/another File.zip', '/'], 72 ['/home/user/a dir//another&File.', '/'], 73 ['/home/user/a$$$dir//another File.zip', '/'], 74 ['user/dir/another File.zip', ''], 75 ['file', ''], 76 ['.\\file', ''], 77 ['./file', ''], 78 ['C:\\foo', ''], 79 ['/', '/'], 80 ['', ''], 81 ['.', ''], 82 ['..', ''], 83 ['/foo', '/'], 84 ['/foo.', '/'], 85 ['/foo.bar', '/'], 86 ['/.', '/'], 87 ['/.foo', '/'], 88 ['/.foo.bar', '/'], 89 ['/foo/bar.baz', '/'], 90]; 91 92const unixSpecialCaseFormatTests = [ 93 [{ dir: 'some/dir' }, 'some/dir/'], 94 [{ base: 'index.html' }, 'index.html'], 95 [{ root: '/' }, '/'], 96 [{ name: 'index', ext: '.html' }, 'index.html'], 97 [{ dir: 'some/dir', name: 'index', ext: '.html' }, 'some/dir/index.html'], 98 [{ root: '/', name: 'index', ext: '.html' }, '/index.html'], 99 [{}, ''], 100]; 101 102const errors = [ 103 { method: 'parse', input: [null] }, 104 { method: 'parse', input: [{}] }, 105 { method: 'parse', input: [true] }, 106 { method: 'parse', input: [1] }, 107 { method: 'parse', input: [] }, 108 { method: 'format', input: [null] }, 109 { method: 'format', input: [''] }, 110 { method: 'format', input: [true] }, 111 { method: 'format', input: [1] }, 112]; 113 114checkParseFormat(path.win32, winPaths); 115checkParseFormat(path.posix, unixPaths); 116checkSpecialCaseParseFormat(path.win32, winSpecialCaseParseTests); 117checkErrors(path.win32); 118checkErrors(path.posix); 119checkFormat(path.win32, winSpecialCaseFormatTests); 120checkFormat(path.posix, unixSpecialCaseFormatTests); 121 122// Test removal of trailing path separators 123const trailingTests = [ 124 [ path.win32.parse, 125 [['.\\', { root: '', dir: '', base: '.', ext: '', name: '.' }], 126 ['\\\\', { root: '\\', dir: '\\', base: '', ext: '', name: '' }], 127 ['\\\\', { root: '\\', dir: '\\', base: '', ext: '', name: '' }], 128 ['c:\\foo\\\\\\', 129 { root: 'c:\\', dir: 'c:\\', base: 'foo', ext: '', name: 'foo' }], 130 ['D:\\foo\\\\\\bar.baz', 131 { root: 'D:\\', 132 dir: 'D:\\foo\\\\', 133 base: 'bar.baz', 134 ext: '.baz', 135 name: 'bar' }, 136 ], 137 ], 138 ], 139 [ path.posix.parse, 140 [['./', { root: '', dir: '', base: '.', ext: '', name: '.' }], 141 ['//', { root: '/', dir: '/', base: '', ext: '', name: '' }], 142 ['///', { root: '/', dir: '/', base: '', ext: '', name: '' }], 143 ['/foo///', { root: '/', dir: '/', base: 'foo', ext: '', name: 'foo' }], 144 ['/foo///bar.baz', 145 { root: '/', dir: '/foo//', base: 'bar.baz', ext: '.baz', name: 'bar' }, 146 ], 147 ], 148 ], 149]; 150const failures = []; 151trailingTests.forEach((test) => { 152 const parse = test[0]; 153 const os = parse === path.win32.parse ? 'win32' : 'posix'; 154 test[1].forEach((test) => { 155 const actual = parse(test[0]); 156 const expected = test[1]; 157 const message = `path.${os}.parse(${JSON.stringify(test[0])})\n expect=${ 158 JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; 159 const actualKeys = Object.keys(actual); 160 const expectedKeys = Object.keys(expected); 161 let failed = (actualKeys.length !== expectedKeys.length); 162 if (!failed) { 163 for (let i = 0; i < actualKeys.length; ++i) { 164 const key = actualKeys[i]; 165 if (!expectedKeys.includes(key) || actual[key] !== expected[key]) { 166 failed = true; 167 break; 168 } 169 } 170 } 171 if (failed) 172 failures.push(`\n${message}`); 173 }); 174}); 175assert.strictEqual(failures.length, 0, failures.join('')); 176 177function checkErrors(path) { 178 errors.forEach(({ method, input }) => { 179 assert.throws(() => { 180 path[method].apply(path, input); 181 }, { 182 code: 'ERR_INVALID_ARG_TYPE', 183 name: 'TypeError' 184 }); 185 }); 186} 187 188function checkParseFormat(path, paths) { 189 paths.forEach(([element, root]) => { 190 const output = path.parse(element); 191 assert.strictEqual(typeof output.root, 'string'); 192 assert.strictEqual(typeof output.dir, 'string'); 193 assert.strictEqual(typeof output.base, 'string'); 194 assert.strictEqual(typeof output.ext, 'string'); 195 assert.strictEqual(typeof output.name, 'string'); 196 assert.strictEqual(path.format(output), element); 197 assert.strictEqual(output.root, root); 198 assert(output.dir.startsWith(output.root)); 199 assert.strictEqual(output.dir, output.dir ? path.dirname(element) : ''); 200 assert.strictEqual(output.base, path.basename(element)); 201 assert.strictEqual(output.ext, path.extname(element)); 202 }); 203} 204 205function checkSpecialCaseParseFormat(path, testCases) { 206 testCases.forEach(([element, expect]) => { 207 assert.deepStrictEqual(path.parse(element), expect); 208 }); 209} 210 211function checkFormat(path, testCases) { 212 testCases.forEach(([element, expect]) => { 213 assert.strictEqual(path.format(element), expect); 214 }); 215 216 [null, undefined, 1, true, false, 'string'].forEach((pathObject) => { 217 assert.throws(() => { 218 path.format(pathObject); 219 }, { 220 code: 'ERR_INVALID_ARG_TYPE', 221 name: 'TypeError', 222 message: 'The "pathObject" argument must be of type object.' + 223 common.invalidArgTypeHelper(pathObject) 224 }); 225 }); 226} 227