From 7019a6b87f3e2b57a3d757ebab102ff6023b88ca Mon Sep 17 00:00:00 2001 From: Chapman Pendery Date: Thu, 15 Feb 2024 10:35:58 -0800 Subject: [PATCH 1/3] feat: add option to hide prompt information during e2e tests Signed-off-by: Chapman Pendery --- shell/shellIntegration-rc.zsh | 3 +++ shell/shellIntegration.bash | 3 +++ shell/shellIntegration.fish | 5 +++++ shell/shellIntegration.ps1 | 7 +++++++ src/commands/root.ts | 3 ++- src/index.ts | 9 ++++++++- src/isterm/pty.ts | 9 ++++++--- src/ui/ui-root.ts | 4 ++-- 8 files changed, 36 insertions(+), 7 deletions(-) diff --git a/shell/shellIntegration-rc.zsh b/shell/shellIntegration-rc.zsh index ca8c943..55dec90 100644 --- a/shell/shellIntegration-rc.zsh +++ b/shell/shellIntegration-rc.zsh @@ -41,6 +41,9 @@ __is_update_cwd() { __is_update_prompt() { __is_prior_prompt="$PS1" + if [ $ISTERM_TESTING == "1" ]; then + __is_prior_prompt = "> " + fi PS1="%{$(__is_prompt_start)%}$PS1%{$(__is_prompt_end)%}" } diff --git a/shell/shellIntegration.bash b/shell/shellIntegration.bash index 9a88bc2..90c0704 100644 --- a/shell/shellIntegration.bash +++ b/shell/shellIntegration.bash @@ -57,6 +57,9 @@ fi __is_update_prompt() { if [[ "$__is_custom_PS1" == "" || "$__is_custom_PS1" != "$PS1" ]]; then __is_original_PS1=$PS1 + if [ $ISTERM_TESTING == "1" ]; then + __is_original_PS1="> " + fi __is_custom_PS1="\[$(__is_prompt_start)\]$__is_original_PS1\[$(__is_prompt_end)\]" export PS1="$__is_custom_PS1" fi diff --git a/shell/shellIntegration.fish b/shell/shellIntegration.fish index 1fcb9bc..d86163e 100644 --- a/shell/shellIntegration.fish +++ b/shell/shellIntegration.fish @@ -11,4 +11,9 @@ end function __is_update_cwd --on-event fish_prompt; set __is_cwd (__is_escape_value "$PWD"); printf "\e]6973;CWD;$__is_cwd\a"; end __is_copy_function fish_prompt is_user_prompt + +if [ "$ISTERM_TESTING" == "1" ] + function is_user_prompt; printf '> '; end +end + function fish_prompt; printf (__is_prompt_start); printf (is_user_prompt); printf (__is_prompt_end); end \ No newline at end of file diff --git a/shell/shellIntegration.ps1 b/shell/shellIntegration.ps1 index ab2e4fb..6aa561f 100644 --- a/shell/shellIntegration.ps1 +++ b/shell/shellIntegration.ps1 @@ -1,5 +1,12 @@ $Global:__IsOriginalPrompt = $function:Prompt +function Global:__IsTestingPrompt() { + return "PS > " +} +if ($env:ISTERM_TESTING -eq "1") { + $Global:__IsOriginalPrompt = $function:__IsTestingPrompt +} + function Global:__IS-Escape-Value([string]$value) { [regex]::Replace($value, '[\\\n;]', { param($match) -Join ( diff --git a/src/commands/root.ts b/src/commands/root.ts index 0acf5aa..5e27637 100644 --- a/src/commands/root.ts +++ b/src/commands/root.ts @@ -14,6 +14,7 @@ type RootCommandOptions = { shell: Shell | undefined; verbose: boolean | undefined; check: boolean | undefined; + test: boolean | undefined; }; export const action = (program: Command) => async (options: RootCommandOptions) => { @@ -39,5 +40,5 @@ export const action = (program: Command) => async (options: RootCommandOptions) } else if (shell == Shell.Bash) { await setupBashPreExec(); } - await render(shell); + await render(shell, options.test ?? false); }; diff --git a/src/index.ts b/src/index.ts index 922d6f1..260bebf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ /* eslint-disable header/header */ -import { Command } from "commander"; +import { Command, Option } from "commander"; import complete from "./commands/complete.js"; import uninstall from "./commands/uninstall.js"; @@ -14,6 +14,12 @@ import { getVersion } from "./utils/version.js"; const program = new Command(); +const hiddenOption = (flags: string, description: string) => { + const option = new Option(flags, description); + option.hidden = true; + return option; +}; + program .name("inshellisense") .description("IDE style command line auto complete") @@ -21,6 +27,7 @@ program .action(action(program)) .option("-s, --shell ", `shell to use for command execution, supported shells: ${supportedShells}`) .option("-c, --check", `check if shell is in an inshellisense session`) + .addOption(hiddenOption("-T, --test", "*private* used to make e2e tests reproducible across machines")) .option("-V, --verbose", `enable verbose logging`) .showHelpAfterError("(add --help for additional information)"); diff --git a/src/isterm/pty.ts b/src/isterm/pty.ts index b96e6dd..889511d 100644 --- a/src/isterm/pty.ts +++ b/src/isterm/pty.ts @@ -25,6 +25,7 @@ type ISTermOptions = { cols: number; shell: Shell; shellArgs?: string[]; + underTest: boolean; }; export class ISTerm implements IPty { @@ -44,13 +45,13 @@ export class ISTerm implements IPty { readonly #commandManager: CommandManager; readonly #shell: Shell; - constructor({ shell, cols, rows, env, shellTarget, shellArgs }: ISTermOptions & { shellTarget: string }) { + constructor({ shell, cols, rows, env, shellTarget, shellArgs, underTest }: ISTermOptions & { shellTarget: string }) { this.#pty = pty.spawn(shellTarget, shellArgs ?? [], { name: "xterm-256color", cols, rows, cwd: process.cwd(), - env: { ...convertToPtyEnv(shell), ...env }, + env: { ...convertToPtyEnv(shell, underTest), ...env }, }); this.pid = this.#pty.pid; this.cols = this.#pty.cols; @@ -264,10 +265,11 @@ const convertToPtyTarget = async (shell: Shell) => { return { shellTarget, shellArgs }; }; -const convertToPtyEnv = (shell: Shell) => { +const convertToPtyEnv = (shell: Shell, underTest: boolean) => { const env = { ...process.env, ISTERM: "1", + ISTERM_TESTING: underTest ? "1" : undefined, }; switch (shell) { case Shell.Cmd: { @@ -278,5 +280,6 @@ const convertToPtyEnv = (shell: Shell) => { return { ...env, ZDOTDIR: zdotdir, USER_ZDOTDIR: userZdotdir }; } } + return env; }; diff --git a/src/ui/ui-root.ts b/src/ui/ui-root.ts index 20d8854..c6da1d9 100644 --- a/src/ui/ui-root.ts +++ b/src/ui/ui-root.ts @@ -16,8 +16,8 @@ export const renderConfirmation = (live: boolean): string => { return `inshellisense session [${statusMessage}]\n`; }; -export const render = async (shell: Shell) => { - const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns }); +export const render = async (shell: Shell, underTest: boolean) => { + const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest }); const suggestionManager = new SuggestionManager(term, shell); let hasActiveSuggestions = false; let previousSuggestionsRows = 0; From 8ced0644d136fddeac894aae5af6057223e11b40 Mon Sep 17 00:00:00 2001 From: Chapman Pendery Date: Thu, 15 Feb 2024 10:37:05 -0800 Subject: [PATCH 2/3] fix: drop hidden option header Signed-off-by: Chapman Pendery --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 260bebf..0d98a49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,7 +27,7 @@ program .action(action(program)) .option("-s, --shell ", `shell to use for command execution, supported shells: ${supportedShells}`) .option("-c, --check", `check if shell is in an inshellisense session`) - .addOption(hiddenOption("-T, --test", "*private* used to make e2e tests reproducible across machines")) + .addOption(hiddenOption("-T, --test", "used to make e2e tests reproducible across machines")) .option("-V, --verbose", `enable verbose logging`) .showHelpAfterError("(add --help for additional information)"); From 219d02d2e9b0fc87091c53855273fb9228a90755 Mon Sep 17 00:00:00 2001 From: Chapman Pendery Date: Thu, 15 Feb 2024 12:03:09 -0800 Subject: [PATCH 3/3] fix: tests to use -T Signed-off-by: Chapman Pendery --- .github/workflows/ci.yml | 31 ------------------- jest.config.cjs | 1 + src/tests/isterm/pty.test.ts | 13 +++----- .../__snapshots__/runtime.test.ts.snap | 14 ++++----- src/tests/runtime/runtime.test.ts | 2 +- 5 files changed, 14 insertions(+), 47 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9864fb7..786b181 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,37 +23,6 @@ jobs: - run: npm run lint - - name: setup pwsh prompt - shell: pwsh - if: matrix.os == 'windows-latest' - run: | - New-Item -Path $profile -ItemType File -Force - Set-Content $profile 'function prompt {' - Add-Content $profile ' $prompt = "PS: $(get-date)> "' - Add-Content $profile ' return "`e]6973;PS`a$prompt`e]6973;PE`a"' - Add-Content $profile '}' - - - name: setup powershell prompt - if: matrix.os == 'windows-latest' - shell: powershell - run: | - New-Item -Path $profile -ItemType File -Force - Set-Content $profile 'function prompt {' - Add-Content $profile ' $ESC = [char]27' - Add-Content $profile ' $BEL = [char]7' - Add-Content $profile ' $prompt = "PS: $(Get-Location)> "' - Add-Content $profile ' return "$ESC]6973;PS$BEL$prompt$ESC]6973;PE$BEL"' - Add-Content $profile '}' - - - name: setup fish prompt - if: matrix.os != 'windows-latest' - shell: bash - run: | - mkdir -p ~/.config/fish/functions && touch ~/.config/fish/functions/fish_prompt.fish - echo "function fish_prompt -d \"Write out the prompt\"" >> ~/.config/fish/functions/fish_prompt.fish - echo " printf '\033]6973;PS\007%s@%s %s%s%s > \033]6973;PE\007' $USER $hostname (set_color $fish_color_cwd) (prompt_pwd) (set_color normal)" >> ~/.config/fish/functions/fish_prompt.fish - echo "end" >> ~/.config/fish/functions/fish_prompt.fish - - name: setup zsh-autosuggestions if: matrix.os != 'windows-latest' shell: bash diff --git a/jest.config.cjs b/jest.config.cjs index d85552c..4ce97c3 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -18,4 +18,5 @@ module.exports = { }, ], }, + testPathIgnorePatterns: ["/node_modules/", "src/tests/ui/", ".tui-test/"], }; diff --git a/src/tests/isterm/pty.test.ts b/src/tests/isterm/pty.test.ts index 61f0971..b30b593 100644 --- a/src/tests/isterm/pty.test.ts +++ b/src/tests/isterm/pty.test.ts @@ -3,17 +3,14 @@ import os from "node:os"; import isterm from "../../isterm"; -import { cursorBackward, IstermPromptEnd, IstermPromptStart } from "../../utils/ansi"; +import { cursorBackward } from "../../utils/ansi"; import { Shell } from "../../utils/shell"; const windowsTest = os.platform() == "win32" ? test.skip : test.skip; const unixTest = os.platform() == "darwin" || os.platform() == "linux" ? test.skip : test.skip; -const bashEnv = { PS1: `${IstermPromptStart}\\u$ ${IstermPromptEnd}` }; -const zshEnv = { PROMPT: `%{${IstermPromptStart}%}%/ %# %{${IstermPromptEnd}%}` }; - const runTerm = async (shell: Shell, input: string[], env?: { [key: string]: string | undefined }) => { - const ptyProcess = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, env }); + const ptyProcess = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, env, underTest: true }); await new Promise((r) => setTimeout(r, 1_000)); for (const data of input) { ptyProcess.write(data); @@ -82,7 +79,7 @@ windowsTest( unixTest( "test bash on initial simple command input", async () => { - const r = await runTerm(Shell.Bash, ["ls\r", "zsh"], bashEnv); + const r = await runTerm(Shell.Bash, ["ls\r", "zsh"]); expect(r).toMatchSnapshot(); }, 10000, @@ -91,7 +88,7 @@ unixTest( unixTest( "test zsh on initial simple command input", async () => { - const r = await runTerm(Shell.Zsh, ["ls\r", "zsh"], zshEnv); + const r = await runTerm(Shell.Zsh, ["ls\r", "zsh"]); expect(r).toMatchSnapshot(); }, 10000, @@ -100,7 +97,7 @@ unixTest( unixTest( "test zsh on suggestion detection", async () => { - const r = await runTerm(Shell.Zsh, ["ls -la\r", "l"], zshEnv); + const r = await runTerm(Shell.Zsh, ["ls -la\r", "l"]); expect(r).toMatchSnapshot(); }, 10000, diff --git a/src/tests/runtime/__snapshots__/runtime.test.ts.snap b/src/tests/runtime/__snapshots__/runtime.test.ts.snap index 3fda0d1..c05c0db 100644 --- a/src/tests/runtime/__snapshots__/runtime.test.ts.snap +++ b/src/tests/runtime/__snapshots__/runtime.test.ts.snap @@ -480,7 +480,7 @@ exports[`parseCommand noOptionsSuggestedDuringVariadicArg 1`] = ` exports[`parseCommand optionsSuggestedAfterVariadicArg 1`] = ` { "argumentDescription": undefined, - "charactersToDrop": 1, + "charactersToDrop": 2, "suggestions": [ { "allNames": [ @@ -494,22 +494,22 @@ exports[`parseCommand optionsSuggestedAfterVariadicArg 1`] = ` }, { "allNames": [ - "-@", + "-L", ], - "description": "Display extended attribute keys and sizes in long (-l) output", + "description": "Follow all symbolic links to final target and list the file or directory the link references rather than the link itself. This option cancels the -P option", "icon": "🔗", "insertValue": undefined, - "name": "-@", + "name": "-L", "priority": 50, }, { "allNames": [ - "-1", + "-l", ], - "description": "(The numeric digit \`\`one''.) Force output to be one entry per line. This is the default when output is not to a terminal", + "description": "(The lowercase letter \`\`ell''.) List in long format. (See below.) A total sum for all the file sizes is output on a line before the long listing", "icon": "🔗", "insertValue": undefined, - "name": "-1", + "name": "-l", "priority": 50, }, ], diff --git a/src/tests/runtime/runtime.test.ts b/src/tests/runtime/runtime.test.ts index abf9e58..212d05d 100644 --- a/src/tests/runtime/runtime.test.ts +++ b/src/tests/runtime/runtime.test.ts @@ -13,7 +13,7 @@ const testData = [ { name: "exclusiveOnOption", command: "ag --affinity --no" }, { name: "providedSuggestion", command: "bw completion --shell " }, { name: "fullyTypedSuggestion", command: "ls -W" }, - { name: "optionsSuggestedAfterVariadicArg", command: "ls item -", maxSuggestions: 3 }, + { name: "optionsSuggestedAfterVariadicArg", command: "ls item -l", maxSuggestions: 3 }, { name: "noOptionsSuggestedDuringVariadicArg", command: "ls -W ite" }, { name: "providedArgDescription", command: "act completion bash -a " }, { name: "completedOptionWithArg", command: "act completion bash -a 'actor' " },