1
0
Fork 0
toolkit/packages/glob/__tests__/internal-globber.test.ts

889 lines
28 KiB
TypeScript
Raw Permalink Normal View History

2019-12-31 15:16:18 +00:00
import * as child from 'child_process'
import * as io from '../../io/src/io'
2020-01-09 20:05:31 +00:00
import * as os from 'os'
2019-12-31 15:16:18 +00:00
import * as path from 'path'
2020-01-09 20:05:31 +00:00
import {Globber, DefaultGlobber} from '../src/internal-globber'
import {GlobOptions} from '../src/internal-glob-options'
2019-12-31 15:16:18 +00:00
import {promises as fs} from 'fs'
const IS_WINDOWS = process.platform === 'win32'
/**
2020-01-09 20:05:31 +00:00
* These test focus on the ability of globber to find files
2019-12-31 15:16:18 +00:00
* and not on the pattern matching aspect
*/
2020-01-09 20:05:31 +00:00
describe('globber', () => {
2019-12-31 15:16:18 +00:00
beforeAll(async () => {
await io.rmRF(getTestTemp())
})
2020-01-09 20:05:31 +00:00
it('captures cwd', async () => {
// Create the following layout:
// first-cwd
// first-cwd/the-correct-file
// second-cwd
// second-cwd/the-wrong-file
const root = path.join(getTestTemp(), 'preserves-cwd')
await fs.mkdir(path.join(root, 'first-cwd'), {recursive: true})
await fs.writeFile(
path.join(root, 'first-cwd', 'the-correct-file.txt'),
'test file content'
)
await fs.mkdir(path.join(root, 'second-cwd'), {recursive: true})
await fs.writeFile(
path.join(root, 'second-cwd', 'the-wrong-file.txt'),
'test file content'
)
const originalCwd = process.cwd()
try {
process.chdir(path.join(root, 'first-cwd'))
const globber = await DefaultGlobber.create('*')
process.chdir(path.join(root, 'second-cwd'))
expect(globber.getSearchPaths()).toEqual([path.join(root, 'first-cwd')])
const itemPaths = await globber.glob()
expect(itemPaths).toEqual([
path.join(root, 'first-cwd', 'the-correct-file.txt')
])
} finally {
process.chdir(originalCwd)
}
})
it('defaults to followSymbolicLinks=true', async () => {
// Create the following layout:
// <root>
// <root>/folder-a
// <root>/folder-a/file
// <root>/symDir -> <root>/folder-a
const root = path.join(
getTestTemp(),
'defaults-to-follow-symbolic-links-true'
)
await fs.mkdir(path.join(root, 'folder-a'), {recursive: true})
await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'folder-a'),
path.join(root, 'symDir')
)
const itemPaths = await glob(root, {})
expect(itemPaths).toEqual([
root,
path.join(root, 'folder-a'),
path.join(root, 'folder-a', 'file'),
path.join(root, 'symDir'),
path.join(root, 'symDir', 'file')
])
})
it('defaults to implicitDescendants=true', async () => {
// Create the following layout:
// <root>
// <root>/folder-a
// <root>/folder-a/file
const root = path.join(
getTestTemp(),
'defaults-to-implicit-descendants-true'
)
await fs.mkdir(path.join(root, 'folder-a'), {recursive: true})
await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content')
const itemPaths = await glob(root, {})
expect(itemPaths).toEqual([
root,
path.join(root, 'folder-a'),
path.join(root, 'folder-a', 'file')
])
})
it('defaults to matchDirectories=true', async () => {
// Create the following layout:
// <root>
// <root>/folder-a
// <root>/folder-a/file
const root = path.join(getTestTemp(), 'defaults-to-match-directories-true')
await fs.mkdir(path.join(root, 'folder-a'), {recursive: true})
await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content')
const itemPaths = await glob(root, {})
expect(itemPaths).toEqual([
root,
path.join(root, 'folder-a'),
path.join(root, 'folder-a', 'file')
])
})
it('does not match file with trailing slash when implicitDescendants=true', async () => {
// Create the following layout:
// <root>
// <root>/file
const root = path.join(
getTestTemp(),
'defaults-to-implicit-descendants-true'
)
const filePath = path.join(root, 'file')
await fs.mkdir(root, {recursive: true})
await fs.writeFile(filePath, 'test file content')
const itemPaths = await glob(`${filePath}/`, {})
expect(itemPaths).toEqual([])
})
2020-01-09 20:05:31 +00:00
it('defaults to omitBrokenSymbolicLinks=true', async () => {
// Create the following layout:
// <root>
// <root>/folder-a
// <root>/folder-a/file
// <root>/symDir -> <root>/no-such
const root = path.join(
getTestTemp(),
'defaults-to-omit-broken-symbolic-links-true'
)
await fs.mkdir(path.join(root, 'folder-a'), {recursive: true})
await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'no-such'),
path.join(root, 'symDir')
)
const itemPaths = await glob(root, {})
expect(itemPaths).toEqual([
root,
path.join(root, 'folder-a'),
path.join(root, 'folder-a', 'file')
])
})
it('detects cycle when followSymbolicLinks=true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/file
// <root>/symDir -> <root>
2020-01-09 20:05:31 +00:00
const root = path.join(getTestTemp(), 'detects-cycle-when-follow-true')
2019-12-31 15:16:18 +00:00
await fs.mkdir(root, {recursive: true})
await fs.writeFile(path.join(root, 'file'), 'test file content')
await createSymlinkDir(root, path.join(root, 'symDir'))
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root, {followSymbolicLinks: true})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([root, path.join(root, 'file')])
})
2020-01-09 20:05:31 +00:00
it('detects deep cycle starting from middle when followSymbolicLinks=true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/file-under-root
// <root>/folder-a
// <root>/folder-a/file-under-a
// <root>/folder-a/folder-b
// <root>/folder-a/folder-b/file-under-b
// <root>/folder-a/folder-b/folder-c
// <root>/folder-a/folder-b/folder-c/file-under-c
// <root>/folder-a/folder-b/folder-c/sym-folder -> <root>
const root = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'detects-deep-cycle-starting-from-middle-when-follow-true'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), {
recursive: true
})
await fs.writeFile(
path.join(root, 'file-under-root'),
'test file under root contents'
)
await fs.writeFile(
path.join(root, 'folder-a', 'file-under-a'),
'test file under a contents'
)
await fs.writeFile(
path.join(root, 'folder-a', 'folder-b', 'file-under-b'),
'test file under b contents'
)
await fs.writeFile(
path.join(root, 'folder-a', 'folder-b', 'folder-c', 'file-under-c'),
'test file under c contents'
)
await createSymlinkDir(
root,
path.join(root, 'folder-a', 'folder-b', 'folder-c', 'sym-folder')
)
await fs.stat(
path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'sym-folder',
'file-under-root'
)
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(path.join(root, 'folder-a', 'folder-b'), {
followSymbolicLinks: true
})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
path.join(root, 'folder-a', 'folder-b'),
path.join(root, 'folder-a', 'folder-b', 'file-under-b'),
path.join(root, 'folder-a', 'folder-b', 'folder-c'),
path.join(root, 'folder-a', 'folder-b', 'folder-c', 'file-under-c'),
path.join(root, 'folder-a', 'folder-b', 'folder-c', 'sym-folder'),
path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'sym-folder',
'file-under-root'
),
path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'sym-folder',
'folder-a'
),
path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'sym-folder',
'folder-a',
'file-under-a'
)
])
})
2020-01-09 20:05:31 +00:00
it('detects cycle starting from symlink when followSymbolicLinks=true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/file
// <root>/symDir -> <root>
const root: string = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'detects-cycle-starting-from-symlink-when-follow-true'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(root, {recursive: true})
await fs.writeFile(path.join(root, 'file'), 'test file content')
await createSymlinkDir(root, path.join(root, 'symDir'))
await fs.stat(path.join(root, 'symDir'))
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(path.join(root, 'symDir'), {
followSymbolicLinks: true
})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
path.join(root, 'symDir'),
path.join(root, 'symDir', 'file')
])
})
2020-01-09 20:05:31 +00:00
it('does not follow symlink when followSymbolicLinks=false', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/realDir
// <root>/realDir/file
// <root>/symDir -> <root>/realDir
const root = path.join(
getTestTemp(),
'does-not-follow-symlink-when-follow-false'
)
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'realDir'),
path.join(root, 'symDir')
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root, {followSymbolicLinks: false})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
root,
path.join(root, 'realDir'),
path.join(root, 'realDir', 'file'),
path.join(root, 'symDir')
])
})
2020-01-09 20:05:31 +00:00
it('does not follow symlink when search path is symlink and followSymbolicLinks=false', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// realDir
// realDir/file
// symDir -> realDir
const root = path.join(
getTestTemp(),
'does-not-follow-symlink-when-search-path-is-symlink-and-follow-false'
)
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'realDir'),
path.join(root, 'symDir')
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(path.join(root, 'symDir'), {
2019-12-31 15:16:18 +00:00
followSymbolicLinks: false
})
expect(itemPaths).toEqual([path.join(root, 'symDir')])
})
2020-01-09 20:05:31 +00:00
it('does not return broken symlink when follow-true and omit-true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/brokenSym -> <root>/noSuch
// <root>/realDir
// <root>/realDir/file
// <root>/symDir -> <root>/realDir
2020-01-09 20:05:31 +00:00
const root = path.join(
getTestTemp(),
'does-not-return-broken-symlink-when-follow-true-and-omit-true'
)
2019-12-31 15:16:18 +00:00
await fs.mkdir(root, {recursive: true})
await createSymlinkDir(
path.join(root, 'noSuch'),
path.join(root, 'brokenSym')
)
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'realDir'),
path.join(root, 'symDir')
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root, {followSymbolicLinks: true})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
root,
path.join(root, 'realDir'),
path.join(root, 'realDir', 'file'),
path.join(root, 'symDir'),
path.join(root, 'symDir', 'file')
])
})
2020-01-09 20:05:31 +00:00
it('does not return broken symlink when search path is broken symlink and followSymbolicLinks=true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/brokenSym -> <root>/noSuch
const root = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'does-not-return-broken-symlink-when-search-path-is-broken-symlink-and-follow-true'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(root, {recursive: true})
const brokenSymPath = path.join(root, 'brokenSym')
await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath)
await fs.lstat(brokenSymPath)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(brokenSymPath, {followSymbolicLinks: true})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([])
})
it('does not return directories when match directories false', async () => {
// Create the following layout:
// <root>/file-1
// <root>/dir-1
// <root>/dir-1/file-2
// <root>/dir-1/dir-2
// <root>/dir-1/dir-2/file-3
const root = path.join(
getTestTemp(),
'does-not-return-directories-when-match-directories-false'
)
await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true})
await fs.writeFile(path.join(root, 'file-1'), '')
await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '')
await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '')
const pattern = `${root}${path.sep}**`
expect(
await glob(pattern, {
matchDirectories: false
})
).toEqual([
path.join(root, 'dir-1', 'dir-2', 'file-3'),
path.join(root, 'dir-1', 'file-2'),
path.join(root, 'file-1')
])
})
2019-12-31 15:16:18 +00:00
it('does not search paths that are not partial matches', async () => {
// Create the following layout:
// <root>
// <root>/realDir
// <root>/realDir/nested
// <root>/realDir/nested/file
// <root>/realDir2
// <root>/realDir2/nested2
// <root>/realDir2/nested2/symDir -> <root>/noSuch
const root = path.join(
getTestTemp(),
'does-not-search-paths-that-are-not-partial-matches'
)
await fs.mkdir(path.join(root, 'realDir', 'nested'), {recursive: true})
await fs.writeFile(
path.join(root, 'realDir', 'nested', 'file'),
'test file content'
)
await fs.mkdir(path.join(root, 'realDir2', 'nested2'), {recursive: true})
await createSymlinkDir(
path.join(root, 'noSuch'),
path.join(root, 'realDir2', 'nested2', 'symDir')
)
2020-01-09 20:05:31 +00:00
const options: GlobOptions = {
2019-12-31 15:16:18 +00:00
followSymbolicLinks: true,
omitBrokenSymbolicLinks: false
}
// Should throw
try {
2020-01-09 20:05:31 +00:00
await glob(`${root}/*Dir*/*nested*/*`, options)
2019-12-31 15:16:18 +00:00
throw new Error('should not reach here')
} catch (err) {
expect(err.message).toMatch(/broken symbolic link/i)
}
// Not partial match
2020-01-09 20:05:31 +00:00
let itemPaths = await glob(`${root}/*Dir/*nested*/*`, options)
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')])
// Not partial match
2020-01-09 20:05:31 +00:00
itemPaths = await glob(`${root}/*Dir*/*nested/*`, options)
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')])
})
2020-01-09 20:05:31 +00:00
it('does not throw for broken symlinks that are not matches or partial matches when followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/realDir
// <root>/realDir/file
// <root>/symDir -> <root>/noSuch
const root = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'does-not-throw-for-broken-symlinks-that-are-not-matches-or-partial-matches-when-follow-true-and-omit-false'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'symDir'))
2020-01-09 20:05:31 +00:00
const options: GlobOptions = {
2019-12-31 15:16:18 +00:00
followSymbolicLinks: true,
omitBrokenSymbolicLinks: false
}
// Match should throw
try {
2020-01-09 20:05:31 +00:00
await glob(`${root}/*`, options)
2019-12-31 15:16:18 +00:00
throw new Error('should not reach here')
} catch (err) {
expect(err.message).toMatch(/broken symbolic link/i)
}
// Partial match should throw
try {
2020-01-09 20:05:31 +00:00
await glob(`${root}/*/*`, options)
2019-12-31 15:16:18 +00:00
throw new Error('should not reach here')
} catch (err) {
expect(err.message).toMatch(/broken symbolic link/i)
}
// Not match or partial match
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(`${root}/*eal*/*`, options)
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([path.join(root, 'realDir', 'file')])
})
2020-01-09 20:05:31 +00:00
it('follows symlink when follow-symbolic-links=true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/realDir
// <root>/realDir/file
// <root>/symDir -> <root>/realDir
const root = path.join(getTestTemp(), 'follows-symlink')
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'realDir'),
path.join(root, 'symDir')
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root, {followSymbolicLinks: true})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
root,
path.join(root, 'realDir'),
path.join(root, 'realDir', 'file'),
path.join(root, 'symDir'),
path.join(root, 'symDir', 'file')
])
})
2020-01-09 20:05:31 +00:00
it('follows symlink when search path is symlink and follow-symbolic-links=true', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// realDir
// realDir/file
// symDir -> realDir
const root = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'follows-symlink-when-search-path-is-symlink-and-follow-true'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'realDir'),
path.join(root, 'symDir')
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(path.join(root, 'symDir'), {
followSymbolicLinks: true
})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
path.join(root, 'symDir'),
path.join(root, 'symDir', 'file')
])
})
it('returns broken symlink when followSymbolicLinks=false', async () => {
// Create the following layout:
// <root>
// <root>/brokenSym -> <root>/noSuch
// <root>/realDir
// <root>/realDir/file
// <root>/symDir -> <root>/realDir
const root = path.join(
getTestTemp(),
'returns-broken-symlink-when-follow-false'
)
await fs.mkdir(root, {recursive: true})
await createSymlinkDir(
path.join(root, 'noSuch'),
path.join(root, 'brokenSym')
)
await fs.mkdir(path.join(root, 'realDir'), {recursive: true})
await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content')
await createSymlinkDir(
path.join(root, 'realDir'),
path.join(root, 'symDir')
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root, {followSymbolicLinks: false})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
root,
path.join(root, 'brokenSym'),
path.join(root, 'realDir'),
path.join(root, 'realDir', 'file'),
path.join(root, 'symDir')
])
})
it('returns broken symlink when search path is broken symlink and followSymbolicLinks=false', async () => {
// Create the following layout:
// <root>
// <root>/brokenSym -> <root>/noSuch
const root = path.join(
getTestTemp(),
'returns-broken-symlink-when-search-path-is-broken-symlink-and-follow-false'
)
await fs.mkdir(root, {recursive: true})
const brokenSymPath = path.join(root, 'brokenSym')
await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(brokenSymPath, {followSymbolicLinks: false})
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([brokenSymPath])
})
it('returns depth first', async () => {
// Create the following layout:
// <root>/a-file
// <root>/b-folder
// <root>/b-folder/a-file
// <root>/b-folder/b-folder
// <root>/b-folder/b-folder/file
// <root>/b-folder/c-file
// <root>/c-file
const root = path.join(getTestTemp(), 'returns-depth-first')
await fs.mkdir(path.join(root, 'b-folder', 'b-folder'), {recursive: true})
await fs.writeFile(path.join(root, 'a-file'), 'test a-file content')
await fs.writeFile(
path.join(root, 'b-folder', 'a-file'),
'test b-folder/a-file content'
)
await fs.writeFile(
path.join(root, 'b-folder', 'b-folder', 'file'),
'test b-folder/b-folder/file content'
)
await fs.writeFile(
path.join(root, 'b-folder', 'c-file'),
'test b-folder/c-file content'
)
await fs.writeFile(path.join(root, 'c-file'), 'test c-file content')
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root)
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
root,
path.join(root, 'a-file'),
path.join(root, 'b-folder'),
path.join(root, 'b-folder', 'a-file'),
path.join(root, 'b-folder', 'b-folder'),
path.join(root, 'b-folder', 'b-folder', 'file'),
path.join(root, 'b-folder', 'c-file'),
path.join(root, 'c-file')
])
})
it('returns descendants', async () => {
// Create the following layout:
// <root>/file-1
// <root>/dir-1
// <root>/dir-1/file-2
// <root>/dir-1/dir-2
// <root>/dir-1/dir-2/file-3
const root = path.join(getTestTemp(), 'returns-descendants')
await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true})
await fs.writeFile(path.join(root, 'file-1'), '')
await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '')
await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '')
// When pattern ends with `/**/`
let pattern = `${root}${path.sep}**${path.sep}`
expect(
2020-01-09 20:05:31 +00:00
await glob(pattern, {
2019-12-31 15:16:18 +00:00
implicitDescendants: false
})
).toHaveLength(3) // sanity check
2020-01-09 20:05:31 +00:00
expect(await glob(pattern)).toEqual([
2019-12-31 15:16:18 +00:00
root,
path.join(root, 'dir-1'),
path.join(root, 'dir-1', 'dir-2'),
path.join(root, 'dir-1', 'dir-2', 'file-3'),
path.join(root, 'dir-1', 'file-2'),
path.join(root, 'file-1')
])
// When pattern ends with something other than `/**/`
pattern = `${root}${path.sep}**${path.sep}dir-?`
expect(
2020-01-09 20:05:31 +00:00
await glob(pattern, {
2019-12-31 15:16:18 +00:00
implicitDescendants: false
})
).toHaveLength(2) // sanity check
2020-01-09 20:05:31 +00:00
expect(await glob(pattern)).toEqual([
2019-12-31 15:16:18 +00:00
path.join(root, 'dir-1'),
path.join(root, 'dir-1', 'dir-2'),
path.join(root, 'dir-1', 'dir-2', 'file-3'),
path.join(root, 'dir-1', 'file-2')
])
})
it('returns directories only when trailing slash and implicit descendants false', async () => {
// Create the following layout:
// <root>/file-1
// <root>/dir-1
// <root>/dir-1/file-2
// <root>/dir-1/dir-2
// <root>/dir-1/dir-2/file-3
const root = path.join(
getTestTemp(),
'returns-directories-only-when-trailing-slash-and-implicit-descendants-false'
)
await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true})
await fs.writeFile(path.join(root, 'file-1'), '')
await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '')
await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '')
const pattern = `${root}${path.sep}**${path.sep}`
2020-01-09 20:05:31 +00:00
expect(await glob(pattern)).toHaveLength(6) // sanity check
2019-12-31 15:16:18 +00:00
expect(
2020-01-09 20:05:31 +00:00
await glob(pattern, {
2019-12-31 15:16:18 +00:00
implicitDescendants: false
})
).toEqual([
root,
path.join(root, 'dir-1'),
path.join(root, 'dir-1', 'dir-2')
])
})
it('returns empty when search path does not exist', async () => {
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(path.join(getTestTemp(), 'nosuch'))
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([])
})
it('returns hidden files', async () => {
// Create the following layout:
// <root>
// <root>/.emptyFolder
// <root>/.file
// <root>/.folder
// <root>/.folder/file
const root = path.join(getTestTemp(), 'returns-hidden-files')
await createHiddenDirectory(path.join(root, '.emptyFolder'))
await createHiddenDirectory(path.join(root, '.folder'))
await createHiddenFile(path.join(root, '.file'), 'test .file content')
await fs.writeFile(
path.join(root, '.folder', 'file'),
'test .folder/file content'
)
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(root)
2019-12-31 15:16:18 +00:00
expect(itemPaths).toEqual([
root,
path.join(root, '.emptyFolder'),
path.join(root, '.file'),
path.join(root, '.folder'),
path.join(root, '.folder', 'file')
])
})
it('returns normalized paths', async () => {
// Create the following layout:
// <root>/hello/world.txt
const root: string = path.join(getTestTemp(), 'returns-normalized-paths')
await fs.mkdir(path.join(root, 'hello'), {recursive: true})
await fs.writeFile(path.join(root, 'hello', 'world.txt'), '')
2020-01-09 20:05:31 +00:00
const itemPaths = await glob(
2019-12-31 15:16:18 +00:00
`${root}${path.sep}${path.sep}${path.sep}hello`
)
expect(itemPaths).toEqual([
path.join(root, 'hello'),
path.join(root, 'hello', 'world.txt')
])
})
2020-01-09 20:05:31 +00:00
it('skips comments', async () => {
const searchPaths = await getSearchPaths(
`#aaa/*${os.EOL}/foo/*${os.EOL}#bbb/*${os.EOL}/bar/*`
)
const drive = IS_WINDOWS ? process.cwd().substr(0, 2) : ''
expect(searchPaths).toEqual([
IS_WINDOWS ? `${drive}\\foo` : '/foo',
IS_WINDOWS ? `${drive}\\bar` : '/bar'
])
})
it('skips empty lines', async () => {
const searchPaths = await getSearchPaths(
`${os.EOL}${os.EOL}/foo/*${os.EOL}${os.EOL}/bar/*${os.EOL}/baz/**${os.EOL}`
)
const drive = IS_WINDOWS ? process.cwd().substr(0, 2) : ''
expect(searchPaths).toEqual([
IS_WINDOWS ? `${drive}\\foo` : '/foo',
IS_WINDOWS ? `${drive}\\bar` : '/bar',
IS_WINDOWS ? `${drive}\\baz` : '/baz'
])
})
it('throws when match broken symlink and followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/brokenSym -> <root>/noSuch
const root = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'throws-when-match-broken-symlink-and-follow-true-and-omit-false'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(root, {recursive: true})
await createSymlinkDir(
path.join(root, 'noSuch'),
path.join(root, 'brokenSym')
)
try {
2020-01-09 20:05:31 +00:00
await glob(root, {
followSymbolicLinks: true,
omitBrokenSymbolicLinks: false
})
2019-12-31 15:16:18 +00:00
throw new Error('Expected tl.find to throw')
} catch (err) {
expect(err.message).toMatch(/broken symbolic link/)
}
})
2020-01-09 20:05:31 +00:00
it('throws when search path is broken symlink and followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => {
2019-12-31 15:16:18 +00:00
// Create the following layout:
// <root>
// <root>/brokenSym -> <root>/noSuch
const root = path.join(
getTestTemp(),
2020-01-09 20:05:31 +00:00
'throws-when-search-path-is-broken-symlink-and-follow-true-and-omit-false'
2019-12-31 15:16:18 +00:00
)
await fs.mkdir(root, {recursive: true})
const brokenSymPath = path.join(root, 'brokenSym')
await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath)
await fs.lstat(brokenSymPath)
try {
2020-01-09 20:05:31 +00:00
await glob(brokenSymPath, {
followSymbolicLinks: true,
omitBrokenSymbolicLinks: false
})
2019-12-31 15:16:18 +00:00
throw new Error('Expected tl.find to throw')
} catch (err) {
expect(err.message).toMatch(/broken symbolic link/)
}
})
})
async function createHiddenDirectory(dir: string): Promise<void> {
if (!path.basename(dir).match(/^\./)) {
throw new Error(`Expected dir '${dir}' to start with '.'.`)
}
await fs.mkdir(dir, {recursive: true})
if (IS_WINDOWS) {
const result = child.spawnSync('attrib.exe', ['+H', dir])
if (result.status !== 0) {
const message: string = (result.output || []).join(' ').trim()
throw new Error(
`Failed to set hidden attribute for directory '${dir}'. ${message}`
)
}
}
}
async function createHiddenFile(file: string, content: string): Promise<void> {
if (!path.basename(file).match(/^\./)) {
throw new Error(`Expected dir '${file}' to start with '.'.`)
}
await fs.mkdir(path.dirname(file), {recursive: true})
await fs.writeFile(file, content)
if (IS_WINDOWS) {
const result = child.spawnSync('attrib.exe', ['+H', file])
if (result.status !== 0) {
const message: string = (result.output || []).join(' ').trim()
throw new Error(
`Failed to set hidden attribute for file '${file}'. ${message}`
)
}
}
}
function getTestTemp(): string {
return path.join(__dirname, '_temp', 'glob')
}
/**
* Creates a symlink directory on OSX/Linux, and a junction point directory on Windows.
* A symlink directory is not created on Windows since it requires an elevated context.
*/
async function createSymlinkDir(real: string, link: string): Promise<void> {
if (IS_WINDOWS) {
await fs.symlink(real, link, 'junction')
} else {
await fs.symlink(real, link)
}
}
2020-01-09 20:05:31 +00:00
async function getSearchPaths(patterns: string): Promise<string[]> {
const globber: Globber = await DefaultGlobber.create(patterns)
return globber.getSearchPaths()
}
async function glob(
patterns: string,
options?: GlobOptions
): Promise<string[]> {
const globber: Globber = await DefaultGlobber.create(patterns, options)
return await globber.glob()
}