2021-04-16 13:01:52 -04:00

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)
})
})
})
})