From 1d6da0eab96598cc780a18ace97d45ec24f9811c Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Tue, 24 Mar 2020 10:07:04 +0800 Subject: [PATCH] add command --- resources/assets/src/scripts/cli.tsx | 6 ++- resources/assets/src/scripts/cli/RmCommand.ts | 44 +++++++++++++++++++ .../tests/scripts/cli/RmCommand.test.ts | 30 +++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 resources/assets/src/scripts/cli/RmCommand.ts create mode 100644 resources/assets/tests/scripts/cli/RmCommand.test.ts diff --git a/resources/assets/src/scripts/cli.tsx b/resources/assets/src/scripts/cli.tsx index d54aa470..7631f965 100644 --- a/resources/assets/src/scripts/cli.tsx +++ b/resources/assets/src/scripts/cli.tsx @@ -6,11 +6,12 @@ import { Shell } from 'blessing-skin-shell' import 'xterm/css/xterm.css' import Draggable from 'react-draggable' import ClosetCommand from './cli/ClosetCommand' +import RmCommand from './cli/RmCommand' import styles from '@/styles/terminal.module.scss' let launched = false -const TerminalWindow: React.FC<{ onClose(): void }> = props => { +const TerminalWindow: React.FC<{ onClose(): void }> = (props) => { const mount = useRef(null) useEffect(() => { @@ -31,8 +32,9 @@ const TerminalWindow: React.FC<{ onClose(): void }> = props => { const shell = new Shell(terminal) shell.addExternal('closet', ClosetCommand) + shell.addExternal('rm', RmCommand) - const unbind = terminal.onData(e => shell.input(e)) + const unbind = terminal.onData((e) => shell.input(e)) launched = true return () => { diff --git a/resources/assets/src/scripts/cli/RmCommand.ts b/resources/assets/src/scripts/cli/RmCommand.ts new file mode 100644 index 00000000..91cb8ae5 --- /dev/null +++ b/resources/assets/src/scripts/cli/RmCommand.ts @@ -0,0 +1,44 @@ +import type { Stdio } from 'blessing-skin-shell' +import { Command } from 'commander' +import * as fetch from '../net' +import { hackStdout, overrideExit } from './configureStdio' + +type Options = { + force?: boolean + recursive?: boolean +} + +export default async function rm(stdio: Stdio, args: string[]) { + const program = new Command() + + /* istanbul ignore next */ + if (process.env.NODE_ENV !== 'test') { + process.stdout = hackStdout(stdio) + overrideExit(program, stdio) + } + + program + .name('rm') + .option( + '-f, --force', + 'ignore nonexistent files and arguments, never prompt', + ) + .option( + '-r, --recursive', + 'remove directories and their contents recursively', + ) + .option('--no-preserve-root', "do not treat '/' specially") + .arguments('') + + const opts: Options = program.parse(args, { from: 'user' }).opts() + const path = program.args[0] + + if (!path) { + stdio.println('rm: missing operand') + stdio.println("Try 'rm --help' for more information.") + } + + if (opts.force && opts.recursive && path.startsWith('/')) { + await fetch.post('/admin/resource?clear-cache') + } +} diff --git a/resources/assets/tests/scripts/cli/RmCommand.test.ts b/resources/assets/tests/scripts/cli/RmCommand.test.ts new file mode 100644 index 00000000..afd1763e --- /dev/null +++ b/resources/assets/tests/scripts/cli/RmCommand.test.ts @@ -0,0 +1,30 @@ +import * as fetch from '@/scripts/net' +import runCommand from '@/scripts/cli/RmCommand' +import { Stdio } from './stdio' + +jest.mock('@/scripts/net') + +test('missing operand', async () => { + const stdio = new Stdio() + await runCommand(stdio, []) + expect(stdio.getStdout()).toInclude('missing operand') + expect(fetch.post).not.toBeCalled() +}) + +test('without "rf"', async () => { + const stdio = new Stdio() + await runCommand(stdio, ['/']) + expect(fetch.post).not.toBeCalled() +}) + +test('not from root', async () => { + const stdio = new Stdio() + await runCommand(stdio, ['-rf', '.']) + expect(fetch.post).not.toBeCalled() +}) + +test('send request', async () => { + const stdio = new Stdio() + await runCommand(stdio, ['-rf', '/']) + expect(fetch.post).toBeCalledWith('/admin/resource?clear-cache') +})