mirror of
https://github.com/jprichardson/node-fs-extra.git
synced 2026-01-25 16:42:57 +00:00
462 lines
15 KiB
JavaScript
462 lines
15 KiB
JavaScript
'use strict'
|
|
|
|
const fs = require('fs')
|
|
const os = require('os')
|
|
const fse = require('../../')
|
|
const path = require('path')
|
|
const assert = require('assert')
|
|
const crypto = require('crypto')
|
|
|
|
/* global afterEach, beforeEach, describe, it */
|
|
|
|
const SIZE = 16 * 64 * 1024 + 7
|
|
let TEST_DIR = ''
|
|
|
|
describe('fs-extra', () => {
|
|
beforeEach(done => {
|
|
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy')
|
|
fse.emptyDir(TEST_DIR, done)
|
|
})
|
|
|
|
afterEach(done => fse.remove(TEST_DIR, done))
|
|
|
|
describe('+ copy()', () => {
|
|
it('should return an error if src and dest are the same', done => {
|
|
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_copy')
|
|
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
|
|
fse.ensureFileSync(fileSrc)
|
|
|
|
fse.copy(fileSrc, fileDest, err => {
|
|
assert.strictEqual(err.message, 'Source and destination must not be the same.')
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should error when overwrite=false and file exists', done => {
|
|
const src = path.join(TEST_DIR, 'src.txt')
|
|
const dest = path.join(TEST_DIR, 'dest.txt')
|
|
|
|
fse.ensureFileSync(src)
|
|
fse.ensureFileSync(dest)
|
|
fse.copy(src, dest, { overwrite: false, errorOnExist: true }, err => {
|
|
assert(err)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should error when overwrite=false and file exists in a dir', done => {
|
|
const src = path.join(TEST_DIR, 'src', 'sfile.txt')
|
|
const dest = path.join(TEST_DIR, 'dest', 'dfile.txt')
|
|
|
|
fse.ensureFileSync(src)
|
|
fse.ensureFileSync(dest)
|
|
fse.copy(src, dest, { overwrite: false, errorOnExist: true }, err => {
|
|
assert(err)
|
|
done()
|
|
})
|
|
})
|
|
|
|
describe('> when src is a file', () => {
|
|
it('should copy the file asynchronously', done => {
|
|
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
|
|
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
|
|
fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
|
|
const srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
|
|
let destMd5 = ''
|
|
|
|
fse.copy(fileSrc, fileDest, err => {
|
|
assert(!err)
|
|
destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
|
|
assert.strictEqual(srcMd5, destMd5)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should work with promises', () => {
|
|
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
|
|
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
|
|
fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
|
|
const srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
|
|
let destMd5 = ''
|
|
|
|
return fse.copy(fileSrc, fileDest).then(() => {
|
|
destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
|
|
assert.strictEqual(srcMd5, destMd5)
|
|
})
|
|
})
|
|
|
|
it('should return an error if src file does not exist', done => {
|
|
const fileSrc = 'we-simply-assume-this-file-does-not-exist.bin'
|
|
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
|
|
|
|
fse.copy(fileSrc, fileDest, err => {
|
|
assert(err)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should copy to a destination file with two \'$\' characters in name (eg: TEST_fs-extra_$$_copy)', done => {
|
|
const fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
|
|
const fileDest = path.join(TEST_DIR, 'TEST_fs-extra_$$_copy')
|
|
|
|
fs.writeFileSync(fileSrc, '')
|
|
|
|
fse.copy(fileSrc, fileDest, err => {
|
|
assert(!err)
|
|
fs.statSync(fileDest)
|
|
done()
|
|
})
|
|
})
|
|
|
|
describe('> when the destination dir does not exist', () => {
|
|
it('should create the destination directory and copy the file', done => {
|
|
const src = path.join(TEST_DIR, 'file.txt')
|
|
const dest = path.join(TEST_DIR, 'this/path/does/not/exist/copied.txt')
|
|
const data = 'did it copy?\n'
|
|
|
|
fs.writeFileSync(src, data, 'utf8')
|
|
|
|
fse.copy(src, dest, err => {
|
|
const data2 = fs.readFileSync(dest, 'utf8')
|
|
assert.strictEqual(data, data2)
|
|
done(err)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when dest exists and is a directory', () => {
|
|
it('should return an error', done => {
|
|
const src = path.join(TEST_DIR, 'file.txt')
|
|
const dest = path.join(TEST_DIR, 'dir')
|
|
fse.ensureFileSync(src)
|
|
fse.ensureDirSync(dest)
|
|
|
|
fse.copy(src, dest, err => {
|
|
assert.strictEqual(err.message, `Cannot overwrite directory '${dest}' with non-directory '${src}'.`)
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when src is a directory', () => {
|
|
describe('> when src directory does not exist', () => {
|
|
it('should return an error', done => {
|
|
const ts = path.join(TEST_DIR, 'this_dir_does_not_exist')
|
|
const td = path.join(TEST_DIR, 'this_dir_really_does_not_matter')
|
|
fse.copy(ts, td, err => {
|
|
assert(err)
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when dest exists and is a file', () => {
|
|
it('should return an error', done => {
|
|
const src = path.join(TEST_DIR, 'src')
|
|
const dest = path.join(TEST_DIR, 'file.txt')
|
|
fs.mkdirSync(src)
|
|
fse.ensureFileSync(dest)
|
|
|
|
fse.copy(src, dest, err => {
|
|
assert.strictEqual(err.message, `Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should preserve symbolic links', done => {
|
|
const src = path.join(TEST_DIR, 'src')
|
|
const dest = path.join(TEST_DIR, 'dest')
|
|
const srcTarget = path.join(TEST_DIR, 'destination')
|
|
fse.mkdirSync(src)
|
|
fse.mkdirSync(srcTarget)
|
|
// symlink type is only used for Windows and the default is 'file'.
|
|
// https://nodejs.org/api/fs.html#fs_fs_symlink_target_path_type_callback
|
|
fse.symlinkSync(srcTarget, path.join(src, 'symlink'), 'dir')
|
|
|
|
fse.copy(src, dest, err => {
|
|
assert.ifError(err)
|
|
const link = fs.readlinkSync(path.join(dest, 'symlink'))
|
|
assert.strictEqual(link, srcTarget)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should copy the directory asynchronously', done => {
|
|
const FILES = 2
|
|
const src = path.join(TEST_DIR, 'src')
|
|
const dest = path.join(TEST_DIR, 'dest')
|
|
|
|
fse.mkdirs(src, err => {
|
|
assert(!err)
|
|
for (let i = 0; i < FILES; ++i) {
|
|
fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
|
|
}
|
|
|
|
const subdir = path.join(src, 'subdir')
|
|
fse.mkdirs(subdir, err => {
|
|
assert(!err)
|
|
for (let i = 0; i < FILES; ++i) {
|
|
fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
|
|
}
|
|
|
|
fse.copy(src, dest, err => {
|
|
assert.ifError(err)
|
|
assert(fs.existsSync(dest))
|
|
|
|
for (let i = 0; i < FILES; ++i) {
|
|
assert(fs.existsSync(path.join(dest, i.toString())))
|
|
}
|
|
|
|
const destSub = path.join(dest, 'subdir')
|
|
for (let j = 0; j < FILES; ++j) {
|
|
assert(fs.existsSync(path.join(destSub, j.toString())))
|
|
}
|
|
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when the destination dir does not exist', () => {
|
|
it('should create the destination directory and copy the file', done => {
|
|
const src = path.join(TEST_DIR, 'data/')
|
|
fse.mkdirsSync(src)
|
|
const d1 = 'file1'
|
|
const d2 = 'file2'
|
|
|
|
fs.writeFileSync(path.join(src, 'f1.txt'), d1)
|
|
fs.writeFileSync(path.join(src, 'f2.txt'), d2)
|
|
|
|
const dest = path.join(TEST_DIR, 'this/path/does/not/exist/outputDir')
|
|
|
|
fse.copy(src, dest, err => {
|
|
const o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
|
|
const o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
|
|
|
|
assert.strictEqual(d1, o1)
|
|
assert.strictEqual(d2, o2)
|
|
|
|
done(err)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when src dir does not exist', () => {
|
|
it('should return an error', done => {
|
|
fse.copy('/does/not/exist', '/something/else', err => {
|
|
assert(err instanceof Error)
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when filter is used', () => {
|
|
it('should do nothing if filter fails', done => {
|
|
const srcDir = path.join(TEST_DIR, 'src')
|
|
const srcFile = path.join(srcDir, 'srcfile.css')
|
|
fse.outputFileSync(srcFile, 'src contents')
|
|
const destDir = path.join(TEST_DIR, 'dest')
|
|
const destFile = path.join(destDir, 'destfile.css')
|
|
const filter = s => path.extname(s) !== '.css' && !fs.statSync(s).isDirectory()
|
|
|
|
fse.copy(srcFile, destFile, filter, err => {
|
|
assert.ifError(err)
|
|
assert(!fs.existsSync(destDir))
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should only copy files allowed by filter fn', done => {
|
|
const srcFile1 = path.join(TEST_DIR, '1.css')
|
|
fs.writeFileSync(srcFile1, '')
|
|
const destFile1 = path.join(TEST_DIR, 'dest1.css')
|
|
const filter = s => s.split('.').pop() !== 'css'
|
|
|
|
fse.copy(srcFile1, destFile1, filter, err => {
|
|
assert(!err)
|
|
assert(!fs.existsSync(destFile1))
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should not call filter fn more than needed', done => {
|
|
const src = path.join(TEST_DIR, 'foo')
|
|
fs.writeFileSync(src, '')
|
|
const dest = path.join(TEST_DIR, 'bar')
|
|
|
|
let filterCallCount = 0
|
|
const filter = () => {
|
|
filterCallCount++
|
|
return true
|
|
}
|
|
|
|
fse.copy(src, dest, filter, err => {
|
|
assert(!err)
|
|
assert.strictEqual(filterCallCount, 1)
|
|
assert(fs.existsSync(dest))
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('accepts options object in place of filter', done => {
|
|
const srcFile1 = path.join(TEST_DIR, '1.jade')
|
|
fs.writeFileSync(srcFile1, '')
|
|
const destFile1 = path.join(TEST_DIR, 'dest1.jade')
|
|
const options = { filter: s => /.html$|.css$/i.test(s) }
|
|
|
|
fse.copy(srcFile1, destFile1, options, (err) => {
|
|
assert(!err)
|
|
assert(!fs.existsSync(destFile1))
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('allows filter fn to return a promise', done => {
|
|
const srcFile1 = path.join(TEST_DIR, '1.css')
|
|
fs.writeFileSync(srcFile1, '')
|
|
const destFile1 = path.join(TEST_DIR, 'dest1.css')
|
|
const filter = s => Promise.resolve(s.split('.').pop() !== 'css')
|
|
|
|
fse.copy(srcFile1, destFile1, filter, err => {
|
|
assert(!err)
|
|
assert(!fs.existsSync(destFile1))
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should apply filter recursively', done => {
|
|
const FILES = 2
|
|
// Don't match anything that ends with a digit higher than 0:
|
|
const filter = s => /(0|\D)$/i.test(s)
|
|
|
|
const src = path.join(TEST_DIR, 'src')
|
|
fse.mkdirsSync(src)
|
|
|
|
for (let i = 0; i < FILES; ++i) {
|
|
fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
|
|
}
|
|
|
|
const subdir = path.join(src, 'subdir')
|
|
fse.mkdirsSync(subdir)
|
|
|
|
for (let i = 0; i < FILES; ++i) {
|
|
fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
|
|
}
|
|
const dest = path.join(TEST_DIR, 'dest')
|
|
fse.copy(src, dest, filter, err => {
|
|
assert(!err)
|
|
|
|
assert(fs.existsSync(dest))
|
|
assert(FILES > 1)
|
|
|
|
for (let i = 0; i < FILES; ++i) {
|
|
if (i === 0) {
|
|
assert(fs.existsSync(path.join(dest, i.toString())))
|
|
} else {
|
|
assert(!fs.existsSync(path.join(dest, i.toString())))
|
|
}
|
|
}
|
|
|
|
const destSub = path.join(dest, 'subdir')
|
|
|
|
for (let j = 0; j < FILES; ++j) {
|
|
if (j === 0) {
|
|
assert(fs.existsSync(path.join(destSub, j.toString())))
|
|
} else {
|
|
assert(!fs.existsSync(path.join(destSub, j.toString())))
|
|
}
|
|
}
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should apply filter to directory names', done => {
|
|
const IGNORE = 'ignore'
|
|
const filter = p => !~p.indexOf(IGNORE)
|
|
|
|
const src = path.join(TEST_DIR, 'src')
|
|
fse.mkdirsSync(src)
|
|
|
|
const ignoreDir = path.join(src, IGNORE)
|
|
fse.mkdirsSync(ignoreDir)
|
|
|
|
fse.writeFileSync(path.join(ignoreDir, 'file'), crypto.randomBytes(SIZE))
|
|
|
|
const dest = path.join(TEST_DIR, 'dest')
|
|
|
|
fse.copy(src, dest, filter, err => {
|
|
assert.ifError(err)
|
|
assert(!fs.existsSync(path.join(dest, IGNORE)), 'directory was not ignored')
|
|
assert(!fs.existsSync(path.join(dest, IGNORE, 'file')), 'file was not ignored')
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should apply filter when it is applied only to dest', done => {
|
|
const timeCond = new Date().getTime()
|
|
|
|
const filter = (s, d) => fs.statSync(d).mtime.getTime() < timeCond
|
|
|
|
const src = path.join(TEST_DIR, 'src')
|
|
fse.mkdirsSync(src)
|
|
const subdir = path.join(src, 'subdir')
|
|
fse.mkdirsSync(subdir)
|
|
|
|
const dest = path.join(TEST_DIR, 'dest')
|
|
|
|
setTimeout(() => {
|
|
fse.mkdirsSync(dest)
|
|
|
|
fse.copy(src, dest, filter, err => {
|
|
assert(!err)
|
|
assert(!fs.existsSync(path.join(dest, 'subdir')))
|
|
done()
|
|
})
|
|
}, 1000)
|
|
})
|
|
|
|
it('should apply filter when it is applied to both src and dest', done => {
|
|
const timeCond = new Date().getTime()
|
|
const filter = (s, d) => s.split('.').pop() !== 'css' && fs.statSync(path.dirname(d)).mtime.getTime() > timeCond
|
|
|
|
const dest = path.join(TEST_DIR, 'dest')
|
|
setTimeout(() => {
|
|
fse.mkdirsSync(dest)
|
|
|
|
const srcFile1 = path.join(TEST_DIR, '1.html')
|
|
const srcFile2 = path.join(TEST_DIR, '2.css')
|
|
const srcFile3 = path.join(TEST_DIR, '3.jade')
|
|
|
|
fse.writeFileSync(srcFile1, '')
|
|
fse.writeFileSync(srcFile2, '')
|
|
fse.writeFileSync(srcFile3, '')
|
|
|
|
const destFile1 = path.join(dest, 'dest1.html')
|
|
const destFile2 = path.join(dest, 'dest2.css')
|
|
const destFile3 = path.join(dest, 'dest3.jade')
|
|
|
|
fse.copy(srcFile1, destFile1, filter, err => {
|
|
assert(!err)
|
|
assert(fs.existsSync(destFile1))
|
|
|
|
fse.copy(srcFile2, destFile2, filter, err => {
|
|
assert(!err)
|
|
assert(!fs.existsSync(destFile2))
|
|
|
|
fse.copy(srcFile3, destFile3, filter, err => {
|
|
assert(!err)
|
|
assert(fs.existsSync(destFile3))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
}, 1000)
|
|
})
|
|
})
|
|
})
|
|
})
|