mirror of
https://github.com/jprichardson/node-fs-extra.git
synced 2026-01-18 16:13:55 +00:00
399 lines
12 KiB
JavaScript
399 lines
12 KiB
JavaScript
'use strict'
|
|
|
|
const fs = require('../../fs')
|
|
const os = require('os')
|
|
const fse = require('../../')
|
|
const path = require('path')
|
|
const assert = require('assert')
|
|
const { differentDevice, ifCrossDeviceEnabled } = require('./cross-device-utils')
|
|
|
|
/* global afterEach, beforeEach, describe, it */
|
|
|
|
const describeIfWindows = process.platform === 'win32' ? describe : describe.skip
|
|
|
|
function createAsyncErrFn (errCode) {
|
|
async function fn () {
|
|
fn.callCount++
|
|
const err = new Error()
|
|
err.code = errCode
|
|
|
|
return Promise.reject(err)
|
|
}
|
|
fn.callCount = 0
|
|
return fn
|
|
}
|
|
|
|
const originalRename = fs.rename
|
|
|
|
function setUpMockFs (errCode) {
|
|
fs.rename = createAsyncErrFn(errCode)
|
|
}
|
|
|
|
function tearDownMockFs () {
|
|
fs.rename = originalRename
|
|
}
|
|
|
|
describe('+ move()', () => {
|
|
let TEST_DIR
|
|
|
|
beforeEach(() => {
|
|
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'move')
|
|
|
|
fse.emptyDirSync(TEST_DIR)
|
|
|
|
// Create fixtures:
|
|
fs.writeFileSync(path.join(TEST_DIR, 'a-file'), 'sonic the hedgehog\n')
|
|
fs.mkdirSync(path.join(TEST_DIR, 'a-folder'))
|
|
fs.writeFileSync(path.join(TEST_DIR, 'a-folder/another-file'), 'tails\n')
|
|
fs.mkdirSync(path.join(TEST_DIR, 'a-folder/another-folder'))
|
|
fs.writeFileSync(path.join(TEST_DIR, 'a-folder/another-folder/file3'), 'knuckles\n')
|
|
})
|
|
|
|
afterEach(done => fse.remove(TEST_DIR, done))
|
|
|
|
describe('> when overwrite = true', () => {
|
|
it('should overwrite file', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-folder', 'another-file')
|
|
|
|
// verify file exists already
|
|
assert(fs.existsSync(dest))
|
|
|
|
fse.move(src, dest, { overwrite: true }, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should overwrite the destination directory', done => {
|
|
// Create src
|
|
const src = path.join(TEST_DIR, 'src')
|
|
fse.ensureDirSync(src)
|
|
fse.mkdirsSync(path.join(src, 'some-folder'))
|
|
fs.writeFileSync(path.join(src, 'some-file'), 'hi')
|
|
|
|
const dest = path.join(TEST_DIR, 'a-folder')
|
|
|
|
// verify dest has stuff in it
|
|
const paths = fs.readdirSync(dest)
|
|
assert(paths.indexOf('another-file') >= 0)
|
|
assert(paths.indexOf('another-folder') >= 0)
|
|
|
|
fse.move(src, dest, { overwrite: true }, err => {
|
|
assert.ifError(err)
|
|
|
|
// verify dest does not have old stuff
|
|
const paths = fs.readdirSync(dest)
|
|
assert.strictEqual(paths.indexOf('another-file'), -1)
|
|
assert.strictEqual(paths.indexOf('another-folder'), -1)
|
|
|
|
// verify dest has new stuff
|
|
assert(paths.indexOf('some-file') >= 0)
|
|
assert(paths.indexOf('some-folder') >= 0)
|
|
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should overwrite folders across devices', done => {
|
|
const src = path.join(TEST_DIR, 'a-folder')
|
|
const dest = path.join(TEST_DIR, 'a-folder-dest')
|
|
|
|
fs.mkdirSync(dest)
|
|
|
|
setUpMockFs('EXDEV')
|
|
|
|
fse.move(src, dest, { overwrite: true }, err => {
|
|
assert.ifError(err)
|
|
assert.strictEqual(fs.rename.callCount, 1)
|
|
|
|
fs.readFile(path.join(dest, 'another-folder', 'file3'), 'utf8', (err, contents) => {
|
|
const expected = /^knuckles\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
tearDownMockFs()
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when overwrite = false', () => {
|
|
it('should rename a file on the same device', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-file-dest')
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should support promises', async () => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-file-dest')
|
|
|
|
await fse.move(src, dest)
|
|
|
|
const contents = fs.readFileSync(dest, 'utf8')
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ok(contents.match(expected))
|
|
})
|
|
|
|
it('should not move a file if source and destination are the same', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = src
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.strictEqual(err.message, 'Source and destination must not be the same.')
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should error if source and destination are the same and source does not exist', done => {
|
|
const src = path.join(TEST_DIR, 'non-existent')
|
|
const dest = src
|
|
|
|
fse.move(src, dest, err => {
|
|
assert(err)
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should not move a directory if source and destination are the same', done => {
|
|
const src = path.join(TEST_DIR, 'a-folder')
|
|
const dest = src
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.strictEqual(err.message, 'Source and destination must not be the same.')
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should not overwrite the destination by default', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-folder', 'another-file')
|
|
|
|
// verify file exists already
|
|
assert(fs.existsSync(dest))
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.strictEqual(err.message, 'dest already exists.')
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should not overwrite if overwrite = false', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-folder', 'another-file')
|
|
|
|
// verify file exists already
|
|
assert(fs.existsSync(dest))
|
|
|
|
fse.move(src, dest, { overwrite: false }, err => {
|
|
assert.strictEqual(err.message, 'dest already exists.')
|
|
done()
|
|
})
|
|
})
|
|
|
|
it('should create directory structure by default', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'does', 'not', 'exist', 'a-file-dest')
|
|
|
|
// verify dest directory does not exist
|
|
assert(!fs.existsSync(path.dirname(dest)))
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should work across devices', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-file-dest')
|
|
|
|
setUpMockFs('EXDEV')
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
assert.strictEqual(fs.rename.callCount, 1)
|
|
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
tearDownMockFs()
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should move folders', done => {
|
|
const src = path.join(TEST_DIR, 'a-folder')
|
|
const dest = path.join(TEST_DIR, 'a-folder-dest')
|
|
|
|
// verify it doesn't exist
|
|
assert(!fs.existsSync(dest))
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(path.join(dest, 'another-file'), 'utf8', (err, contents) => {
|
|
const expected = /^tails\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should move folders across devices with EXDEV error', done => {
|
|
const src = path.join(TEST_DIR, 'a-folder')
|
|
const dest = path.join(TEST_DIR, 'a-folder-dest')
|
|
|
|
setUpMockFs('EXDEV')
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
assert.strictEqual(fs.rename.callCount, 1)
|
|
|
|
fs.readFile(path.join(dest, 'another-folder', 'file3'), 'utf8', (err, contents) => {
|
|
const expected = /^knuckles\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
tearDownMockFs()
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when opts is explicit undefined', () => {
|
|
it('works with callbacks', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-file-dest')
|
|
|
|
fse.move(src, dest, undefined, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
it('works with promises', async () => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-file-dest')
|
|
|
|
await fse.move(src, dest, undefined)
|
|
|
|
const contents = fs.readFileSync(dest, 'utf8')
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ok(contents.match(expected))
|
|
})
|
|
})
|
|
|
|
describeIfWindows('> when dest parent is root', () => {
|
|
let dest
|
|
|
|
afterEach(done => fse.remove(dest, done))
|
|
|
|
it('should not create parent directory', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
dest = path.join(path.parse(TEST_DIR).root, 'another-file')
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> clobber', () => {
|
|
it('should be an alias for overwrite', done => {
|
|
const src = path.join(TEST_DIR, 'a-file')
|
|
const dest = path.join(TEST_DIR, 'a-folder', 'another-file')
|
|
|
|
// verify file exists already
|
|
assert(fs.existsSync(dest))
|
|
|
|
fse.move(src, dest, { clobber: true }, err => {
|
|
assert.ifError(err)
|
|
fs.readFile(dest, 'utf8', (err, contents) => {
|
|
const expected = /^sonic the hedgehog\r?\n$/
|
|
assert.ifError(err)
|
|
assert.ok(contents.match(expected))
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('> when trying to move a folder into itself', () => {
|
|
it('should produce an error', done => {
|
|
const SRC_DIR = path.join(TEST_DIR, 'test')
|
|
const DEST_DIR = path.join(TEST_DIR, 'test', 'test')
|
|
|
|
assert(!fs.existsSync(SRC_DIR))
|
|
fs.mkdirSync(SRC_DIR)
|
|
assert(fs.existsSync(SRC_DIR))
|
|
|
|
fse.move(SRC_DIR, DEST_DIR, err => {
|
|
assert(fs.existsSync(SRC_DIR))
|
|
assert.strictEqual(err.message, `Cannot move '${SRC_DIR}' to a subdirectory of itself, '${DEST_DIR}'.`)
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
|
|
// tested on Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP i686 i686 GNU/Linux
|
|
// this won't trigger a bug on Mac OS X Yosimite with a USB drive (/Volumes)
|
|
// see issue #108
|
|
ifCrossDeviceEnabled(describe)('> when actually trying to move a folder across devices', () => {
|
|
describe('>> just the folder', () => {
|
|
it('should move the folder', done => {
|
|
const src = path.join(differentDevice, 'some/weird/dir-really-weird')
|
|
const dest = path.join(TEST_DIR, 'device-weird')
|
|
|
|
if (!fs.existsSync(src)) {
|
|
fse.mkdirpSync(src)
|
|
}
|
|
|
|
assert(!fs.existsSync(dest))
|
|
|
|
assert(fs.lstatSync(src).isDirectory())
|
|
|
|
fse.move(src, dest, err => {
|
|
assert.ifError(err)
|
|
assert(fs.existsSync(dest))
|
|
assert(fs.lstatSync(dest).isDirectory())
|
|
done()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|