mirror of
https://github.com/shelljs/shelljs.git
synced 2026-01-18 16:03:37 +00:00
311 lines
11 KiB
JavaScript
311 lines
11 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import test from 'ava';
|
|
|
|
import shell from '..';
|
|
import utils from './utils/utils';
|
|
|
|
test.beforeEach(t => {
|
|
t.context.tmp = utils.getTempDir();
|
|
shell.config.resetForTesting();
|
|
shell.cp('-r', 'test/resources', t.context.tmp);
|
|
});
|
|
|
|
test.afterEach.always(t => {
|
|
shell.rm('-rf', t.context.tmp);
|
|
});
|
|
|
|
|
|
//
|
|
// Invalids
|
|
//
|
|
|
|
test('no args', t => {
|
|
const result = shell.rm();
|
|
t.truthy(shell.error());
|
|
t.is(result.code, 1);
|
|
t.is(result.stderr, 'rm: no paths given');
|
|
});
|
|
|
|
test('file does not exist', t => {
|
|
const result = shell.rm('asdfasdf');
|
|
t.truthy(shell.error());
|
|
t.is(result.code, 1);
|
|
t.is(result.stderr, 'rm: no such file or directory: asdfasdf');
|
|
});
|
|
|
|
test('cannot delete a directory without recursive flag', t => {
|
|
const result = shell.rm(`${t.context.tmp}/rm`);
|
|
t.truthy(shell.error());
|
|
t.is(result.code, 1);
|
|
t.is(result.stderr, 'rm: path is a directory');
|
|
});
|
|
|
|
test('only an option', t => {
|
|
const result = shell.rm('-f');
|
|
t.truthy(shell.error());
|
|
t.is(result.code, 1);
|
|
t.is(result.stderr, 'rm: no paths given');
|
|
});
|
|
|
|
test('invalid option', t => {
|
|
const result = shell.rm('-@', 'test/resources/file1');
|
|
t.truthy(shell.error());
|
|
t.is(result.code, 1);
|
|
t.truthy(fs.existsSync('test/resources/file1'));
|
|
t.is(result.stderr, 'rm: option not recognized: @');
|
|
});
|
|
|
|
//
|
|
// Valids
|
|
//
|
|
|
|
test('file does not exist, but -f specified', t => {
|
|
const result = shell.rm('-f', 'asdfasdf');
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
});
|
|
|
|
test('directory does not exist, but -fr specified', t => {
|
|
const result = shell.rm('-fr', 'fake_dir/');
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
});
|
|
|
|
test('directory does not exist, but *only -f* specified', t => {
|
|
const result = shell.rm('-f', 'fake_dir/');
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
});
|
|
|
|
test('file (in fake dir) does not exist, but -f specified', t => {
|
|
const result = shell.rm('-f', 'fake_dir/asdfasdf');
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
});
|
|
|
|
test('dir (in fake dir) does not exist, but -fr specified', t => {
|
|
const result = shell.rm('-fr', 'fake_dir/sub/');
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
});
|
|
|
|
test('simple rm', t => {
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/file1`));
|
|
const result = shell.rm(`${t.context.tmp}/file1`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/file1`));
|
|
});
|
|
|
|
test('recursive dir removal: -r option', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
const result = shell.rm('-rf', `${t.context.tmp}/a`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/a`));
|
|
});
|
|
|
|
test('-R option does the same thing', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
const result = shell.rm('-Rf', `${t.context.tmp}/a`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/a`));
|
|
});
|
|
|
|
test('recursive dir removal - absolute path', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
const result = shell.rm('-Rf', path.resolve(`./${t.context.tmp}/a`));
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/a`));
|
|
});
|
|
|
|
test('wildcard', t => {
|
|
const result = shell.rm(`${t.context.tmp}/file*`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/file1`));
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/file2`));
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/file1.js`));
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/file2.js`));
|
|
});
|
|
|
|
test('recursive dir removal', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
shell.mkdir('-p', `${t.context.tmp}/b`);
|
|
shell.mkdir('-p', `${t.context.tmp}/c`);
|
|
shell.mkdir('-p', `${t.context.tmp}/.hidden`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/b`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/c`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/.hidden`));
|
|
const result = shell.rm('-rf', `${t.context.tmp}/*`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
const contents = fs.readdirSync(t.context.tmp);
|
|
t.is(contents.length, 1);
|
|
t.is(contents[0], '.hidden'); // shouldn't remove hidden if no .* given
|
|
});
|
|
|
|
test('recursive dir removal #2', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
shell.mkdir('-p', `${t.context.tmp}/b`);
|
|
shell.mkdir('-p', `${t.context.tmp}/c`);
|
|
shell.mkdir('-p', `${t.context.tmp}/.hidden`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/b`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/c`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/.hidden`));
|
|
const result = shell.rm('-rf', `${t.context.tmp}/*`, `${t.context.tmp}/.*`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
const contents = fs.readdirSync(t.context.tmp);
|
|
t.is(contents.length, 0);
|
|
});
|
|
|
|
test('recursive dir removal - array-syntax', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
shell.mkdir('-p', `${t.context.tmp}/b`);
|
|
shell.mkdir('-p', `${t.context.tmp}/c`);
|
|
shell.mkdir('-p', `${t.context.tmp}/.hidden`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/b`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/c`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/.hidden`));
|
|
const result = shell.rm('-rf', [`${t.context.tmp}/*`, `${t.context.tmp}/.*`]);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
const contents = fs.readdirSync(t.context.tmp);
|
|
t.is(contents.length, 0);
|
|
});
|
|
|
|
test('removal of a read-only file (unforced)', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/readonly`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/readonly/file1`);
|
|
fs.chmodSync(`${t.context.tmp}/readonly/file1`, '0444'); // -r--r--r--
|
|
shell.rm(`${t.context.tmp}/readonly/file1`);
|
|
t.truthy(shell.error());
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/readonly/file1`)); // bash's rm always asks before removing read-only files
|
|
// here we just assume "no"
|
|
});
|
|
|
|
test('removal of a read-only file (forced)', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/readonly`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/readonly/file2`);
|
|
fs.chmodSync(`${t.context.tmp}/readonly/file2`, '0444'); // -r--r--r--
|
|
shell.rm('-f', `${t.context.tmp}/readonly/file2`);
|
|
t.falsy(shell.error());
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/readonly/file2`));
|
|
});
|
|
|
|
test('removal of a tree containing read-only files (unforced)', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/tree2`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree2/file1`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree2/file2`);
|
|
fs.chmodSync(`${t.context.tmp}/tree2/file1`, '0444'); // -r--r--r--
|
|
shell.rm('-r', `${t.context.tmp}/tree2`);
|
|
t.truthy(shell.error());
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/tree2/file1`));
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/tree2/file2`));
|
|
});
|
|
|
|
test('removal of a tree containing read-only files (forced)', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/tree`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree/file1`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree/file2`);
|
|
fs.chmodSync(`${t.context.tmp}/tree/file1`, '0444'); // -r--r--r--
|
|
shell.rm('-rf', `${t.context.tmp}/tree`);
|
|
t.falsy(shell.error());
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/tree`));
|
|
});
|
|
|
|
test(
|
|
'removal of a sub-tree containing read-only and hidden files - glob',
|
|
t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/tree3`);
|
|
shell.mkdir('-p', `${t.context.tmp}/tree3/subtree`);
|
|
shell.mkdir('-p', `${t.context.tmp}/tree3/.hidden`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree3/subtree/file`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree3/.hidden/file`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree3/file`);
|
|
fs.chmodSync(`${t.context.tmp}/tree3/file`, '0444'); // -r--r--r--
|
|
fs.chmodSync(`${t.context.tmp}/tree3/subtree/file`, '0444'); // -r--r--r--
|
|
fs.chmodSync(`${t.context.tmp}/tree3/.hidden/file`, '0444'); // -r--r--r--
|
|
shell.rm('-rf', `${t.context.tmp}/tree3/*`, `${t.context.tmp}/tree3/.*`); // erase dir contents
|
|
t.is(shell.ls(`${t.context.tmp}/tree3`).length, 0);
|
|
}
|
|
);
|
|
|
|
test(
|
|
'removal of a sub-tree containing read-only and hidden files - without glob',
|
|
t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/tree4`);
|
|
shell.mkdir('-p', `${t.context.tmp}/tree4/subtree`);
|
|
shell.mkdir('-p', `${t.context.tmp}/tree4/.hidden`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree4/subtree/file`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree4/.hidden/file`);
|
|
shell.ShellString('asdf').to(`${t.context.tmp}/tree4/file`);
|
|
fs.chmodSync(`${t.context.tmp}/tree4/file`, '0444'); // -r--r--r--
|
|
fs.chmodSync(`${t.context.tmp}/tree4/subtree/file`, '0444'); // -r--r--r--
|
|
fs.chmodSync(`${t.context.tmp}/tree4/.hidden/file`, '0444'); // -r--r--r--
|
|
shell.rm('-rf', `${t.context.tmp}/tree4`); // erase dir contents
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/tree4`));
|
|
}
|
|
);
|
|
|
|
test('remove symbolic link to a dir', t => {
|
|
const result = shell.rm(`${t.context.tmp}/rm/link_to_a_dir`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/rm/link_to_a_dir`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/rm/a_dir`));
|
|
});
|
|
|
|
test('rm -rf on a symbolic link to a dir deletes its contents', t => {
|
|
utils.skipOnWin(t, () => {
|
|
const result = shell.rm('-rf', `${t.context.tmp}/rm/link_to_a_dir/`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
|
|
// Both the link and original dir should remain, but contents are deleted
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/rm/link_to_a_dir`));
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/rm/a_dir`));
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/rm/a_dir/a_file`));
|
|
});
|
|
});
|
|
|
|
test('remove broken symbolic link', t => {
|
|
utils.skipOnWin(t, () => {
|
|
t.truthy(shell.test('-L', `${t.context.tmp}/rm/fake.lnk`));
|
|
const result = shell.rm(`${t.context.tmp}/rm/fake.lnk`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(shell.test('-L', `${t.context.tmp}/rm/fake.lnk`));
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/rm/fake.lnk`));
|
|
});
|
|
});
|
|
|
|
test('recursive dir removal, for non-normalized path', t => {
|
|
shell.mkdir('-p', `${t.context.tmp}/a/b/c`);
|
|
t.truthy(fs.existsSync(`${t.context.tmp}/a/b/c`));
|
|
const result = shell.rm('-rf', `${t.context.tmp}/a/.././a`);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
t.falsy(fs.existsSync(`${t.context.tmp}/a`));
|
|
});
|
|
|
|
test('remove fifo', t => {
|
|
utils.skipOnWin(t, () => {
|
|
const fifo = utils.mkfifo(t.context.tmp);
|
|
const result = shell.rm(fifo);
|
|
t.falsy(shell.error());
|
|
t.is(result.code, 0);
|
|
});
|
|
});
|