From 78984b48c44ce2d4f7603081004a0fad0f2aa1e7 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Thu, 22 Aug 2024 20:23:00 -0400 Subject: [PATCH 01/11] add more tests for cli prompt text options default value --- packages/cli/test/Prompt.test.ts | 88 ++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index 9f6f771a12..abd1c547f6 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -3,6 +3,7 @@ import * as MockTerminal from "@effect/cli/test/services/MockTerminal" import type { Terminal } from "@effect/platform/Terminal" import * as Effect from "effect/Effect" import * as Fiber from "effect/Fiber" +import * as Redacted from "effect/Redacted" import { describe, expect, it } from "vitest" const runEffect = ( @@ -38,4 +39,91 @@ describe("Prompt", () => { expect(result).toBe("default-value") }).pipe(runEffect)) }) + + describe("hidden", () => { + it("should use the prompt value when no default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.hidden({ + message: "This does not have a default" + }) + + const fiber = yield* Effect.fork(prompt) + yield* MockTerminal.inputKey("enter") + const result = yield* Fiber.join(fiber) + + expect(result).toEqual(Redacted.make("")) + }).pipe(runEffect)) + + it("should use the default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.hidden({ + message: "This should have a default", + default: "default-value" + }) + + const fiber = yield* Effect.fork(prompt) + yield* MockTerminal.inputKey("enter") + const result = yield* Fiber.join(fiber) + + expect(result).toEqual(Redacted.make("default-value")) + }).pipe(runEffect)) + }) + + describe("list", () => { + it("should use the prompt value when no default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.list({ + message: "This does not have a default" + }) + + const fiber = yield* Effect.fork(prompt) + yield* MockTerminal.inputKey("enter") + const result = yield* Fiber.join(fiber) + + expect(result).toEqual(['']) + }).pipe(runEffect)) + + it("should use the default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.list({ + message: "This should have a default", + default: "default-value" + }) + + const fiber = yield* Effect.fork(prompt) + yield* MockTerminal.inputKey("enter") + const result = yield* Fiber.join(fiber) + + expect(result).toEqual(["default-value"]) + }).pipe(runEffect)) + }) + + describe("password", () => { + it("should use the prompt value when no default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.password({ + message: "This does not have a default" + }) + + const fiber = yield* Effect.fork(prompt) + yield* MockTerminal.inputKey("enter") + const result = yield* Fiber.join(fiber) + + expect(result).toEqual(Redacted.make("")) + }).pipe(runEffect)) + + it("should use the default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.password({ + message: "This should have a default", + default: "default-value" + }) + + const fiber = yield* Effect.fork(prompt) + yield* MockTerminal.inputKey("enter") + const result = yield* Fiber.join(fiber) + + expect(result).toEqual(Redacted.make("default-value")) + }).pipe(runEffect)) + }) }) From 4f79ddd728dda38f20184fa0aaa82b035978c88f Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Thu, 22 Aug 2024 20:23:35 -0400 Subject: [PATCH 02/11] render default values in bright black for text prompts --- packages/cli/src/internal/prompt/text.ts | 38 +++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/internal/prompt/text.ts b/packages/cli/src/internal/prompt/text.ts index bbc27086d4..7646325797 100644 --- a/packages/cli/src/internal/prompt/text.ts +++ b/packages/cli/src/internal/prompt/text.ts @@ -4,6 +4,7 @@ import * as Doc from "@effect/printer-ansi/AnsiDoc" import * as Optimize from "@effect/printer/Optimize" import * as Arr from "effect/Array" import * as Effect from "effect/Effect" +import * as Match from "effect/Match" import * as Option from "effect/Option" import * as Redacted from "effect/Redacted" import type * as Prompt from "../../Prompt.js" @@ -25,6 +26,10 @@ interface State { readonly error: Option.Option } +function getValue(state: State, options: Options): string { + return state.value.length > 0 ? state.value : options.default +} + const renderBeep = Doc.render(Doc.beep, { style: "pretty" }) function renderClearScreen(state: State, options: Options) { @@ -58,19 +63,30 @@ function renderClearScreen(state: State, options: Options) { } function renderInput(nextState: State, options: Options, submitted: boolean) { + const text = getValue(nextState, options) + const annotation = Option.match(nextState.error, { - onNone: () => submitted ? Ansi.white : Ansi.combine(Ansi.underlined, Ansi.cyanBright), + onNone: () => Match.value(submitted) + .pipe( + Match.when(true, () => Ansi.green), + Match.orElse(() => Match.value(nextState.value).pipe( + Match.when('', () => Ansi.blackBright), + Match.orElse(() => Ansi.combine(Ansi.underlined, Ansi.cyanBright)) + )) + ), onSome: () => Ansi.red }) + + switch (options.type) { case "hidden": { return Doc.empty } case "password": { - return Doc.annotate(Doc.text("*".repeat(nextState.value.length)), annotation) + return Doc.annotate(Doc.text("*".repeat(text.length)), annotation) } case "text": { - return Doc.annotate(Doc.text(nextState.value), annotation) + return Doc.annotate(Doc.text(text), annotation) } } } @@ -191,6 +207,17 @@ function processCursorRight(state: State) { })) } +function processTab(state: State, options: Options) { + if (state.value === options.default) { + return Effect.succeed(Action.Beep()) + } + const value = getValue(state, options) + const cursor = value.length + return Effect.succeed(Action.NextFrame({ + state: { ...state, value, cursor, error: Option.none() } + })) +} + function defaultProcessor(input: string, state: State) { const beforeCursor = state.value.slice(0, state.cursor) const afterCursor = state.value.slice(state.cursor) @@ -232,7 +259,7 @@ function handleProcess(options: Options) { } case "enter": case "return": { - const value = state.value.length > 0 ? state.value : options.default + const value = getValue(state, options) return Effect.match(options.validate(value), { onFailure: (error) => Action.NextFrame({ @@ -241,6 +268,9 @@ function handleProcess(options: Options) { onSuccess: (value) => Action.Submit({ value }) }) } + case "tab": { + return processTab(state, options); + } default: { const value = Option.getOrElse(input.input, () => "") return defaultProcessor(value, state) From f7c5e44532bdccbc2049bdbca10a5a14f398da70 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Thu, 22 Aug 2024 23:27:25 -0400 Subject: [PATCH 03/11] prompt default value render test --- packages/cli/src/internal/prompt/text.ts | 24 +++++----- packages/cli/test/Prompt.test.ts | 56 ++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/internal/prompt/text.ts b/packages/cli/src/internal/prompt/text.ts index 7646325797..7549019ca2 100644 --- a/packages/cli/src/internal/prompt/text.ts +++ b/packages/cli/src/internal/prompt/text.ts @@ -66,18 +66,20 @@ function renderInput(nextState: State, options: Options, submitted: boolean) { const text = getValue(nextState, options) const annotation = Option.match(nextState.error, { - onNone: () => Match.value(submitted) - .pipe( - Match.when(true, () => Ansi.green), - Match.orElse(() => Match.value(nextState.value).pipe( - Match.when('', () => Ansi.blackBright), - Match.orElse(() => Ansi.combine(Ansi.underlined, Ansi.cyanBright)) - )) - ), + onNone: () => + Match.value(submitted) + .pipe( + Match.when(true, () => Ansi.green), + Match.orElse(() => + Match.value(nextState.value).pipe( + Match.when("", () => Ansi.blackBright), + Match.orElse(() => Ansi.combine(Ansi.underlined, Ansi.cyanBright)) + ) + ) + ), onSome: () => Ansi.red }) - switch (options.type) { case "hidden": { return Doc.empty @@ -259,7 +261,7 @@ function handleProcess(options: Options) { } case "enter": case "return": { - const value = getValue(state, options) + const value = getValue(state, options) return Effect.match(options.validate(value), { onFailure: (error) => Action.NextFrame({ @@ -269,7 +271,7 @@ function handleProcess(options: Options) { }) } case "tab": { - return processTab(state, options); + return processTab(state, options) } default: { const value = Option.getOrElse(input.input, () => "") diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index abd1c547f6..5afdf06c3c 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -1,14 +1,31 @@ +import type * as CliApp from "@effect/cli/CliApp" +import * as Command from "@effect/cli/Command" import * as Prompt from "@effect/cli/Prompt" +import * as MockConsole from "@effect/cli/test/services/MockConsole" import * as MockTerminal from "@effect/cli/test/services/MockTerminal" -import type { Terminal } from "@effect/platform/Terminal" +import { NodeFileSystem, NodePath } from "@effect/platform-node" +import * as Ansi from "@effect/printer-ansi/Ansi" +import * as Doc from "@effect/printer-ansi/AnsiDoc" +import * as Console from "effect/Console" import * as Effect from "effect/Effect" import * as Fiber from "effect/Fiber" +import * as Layer from "effect/Layer" import * as Redacted from "effect/Redacted" import { describe, expect, it } from "vitest" +const MainLive = Effect.gen(function*(_) { + const console = yield* _(MockConsole.make) + return Layer.mergeAll( + Console.setConsole(console), + NodeFileSystem.layer, + MockTerminal.layer, + NodePath.layer + ) +}).pipe(Layer.unwrapEffect) + const runEffect = ( - self: Effect.Effect -): Promise => Effect.provide(self, MockTerminal.layer).pipe(Effect.runPromise) + self: Effect.Effect +): Promise => Effect.provide(self, MainLive).pipe(Effect.runPromise) describe("Prompt", () => { describe("text", () => { @@ -38,6 +55,37 @@ describe("Prompt", () => { expect(result).toBe("default-value") }).pipe(runEffect)) + + it("should render the default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.text({ + message: "Test Prompt", + default: "default-value" + }) + + const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ + name: "Test", + version: "1.0.0" + })) + + const fiber = yield* Effect.fork(cli([])) + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + const lines = yield* MockConsole.getLines() + + const [line1, , line2] = lines.filter(Boolean) + + expect(line1).toContain( + Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + style: "pretty" + })) + ) + expect(line2).toContain( + Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + style: "pretty" + })) + ) + }).pipe(runEffect)) }) describe("hidden", () => { @@ -80,7 +128,7 @@ describe("Prompt", () => { yield* MockTerminal.inputKey("enter") const result = yield* Fiber.join(fiber) - expect(result).toEqual(['']) + expect(result).toEqual([""]) }).pipe(runEffect)) it("should use the default value when the default is provided", () => From ecf29454759cf18dc4a12338285fa7445d0a293f Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Tue, 27 Aug 2024 22:38:47 -0400 Subject: [PATCH 04/11] better tests for prompt default rendering --- packages/cli/test/Prompt.test.ts | 145 +++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 9 deletions(-) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index 5afdf06c3c..5bd9652295 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -69,21 +69,33 @@ describe("Prompt", () => { })) const fiber = yield* Effect.fork(cli([])) + yield* MockTerminal.inputKey("enter") yield* Fiber.join(fiber) + const lines = yield* MockConsole.getLines() - const [line1, , line2] = lines.filter(Boolean) + const uncommittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + style: "pretty" + })) + + const committedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + style: "pretty" + })) - expect(line1).toContain( - Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ - style: "pretty" - })) + expect(lines).toEqual( + expect.arrayContaining([ + expect.stringContaining( + uncommittedValue + ), + expect.stringContaining( + committedValue + ) + ]) ) - expect(line2).toContain( - Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ - style: "pretty" - })) + + expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(committedValue)) ) }).pipe(runEffect)) }) @@ -115,6 +127,34 @@ describe("Prompt", () => { expect(result).toEqual(Redacted.make("default-value")) }).pipe(runEffect)) + + it("should not render the default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.hidden({ + message: "Test Prompt", + default: "default-value" + }) + + const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ + name: "Test", + version: "1.0.0" + })) + + const fiber = yield* Effect.fork(cli([])) + + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + + const lines = yield* MockConsole.getLines({ stripAnsi: true }) + + expect(lines).not.toEqual( + expect.arrayContaining([ + expect.stringContaining( + "default-value" + ) + ]) + ) + }).pipe(runEffect)) }) describe("list", () => { @@ -144,6 +184,49 @@ describe("Prompt", () => { expect(result).toEqual(["default-value"]) }).pipe(runEffect)) + + it("should render the default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.list({ + message: "Test Prompt", + default: "default-value" + }) + + const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ + name: "Test", + version: "1.0.0" + })) + + const fiber = yield* Effect.fork(cli([])) + + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + + const lines = yield* MockConsole.getLines() + + const uncommittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + style: "pretty" + })) + + const committedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + style: "pretty" + })) + + expect(lines).toEqual( + expect.arrayContaining([ + expect.stringContaining( + uncommittedValue + ), + expect.stringContaining( + committedValue + ) + ]) + ) + + expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(committedValue)) + ) + }).pipe(runEffect)) }) describe("password", () => { @@ -173,5 +256,49 @@ describe("Prompt", () => { expect(result).toEqual(Redacted.make("default-value")) }).pipe(runEffect)) + + it("should render the redacted default value when the default is provided", () => + Effect.gen(function*() { + const prompt = Prompt.password({ + message: "Test Prompt", + default: "default-value" + }) + + const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ + name: "Test", + version: "1.0.0" + })) + + const fiber = yield* Effect.fork(cli([])) + + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + + const lines = yield* MockConsole.getLines() + + const redactedValue = Array.from("default-value", () => "*").join("") + const uncommittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.blackBright).pipe(Doc.render({ + style: "pretty" + })) + + const committedValue = Doc.annotate(Doc.text(redactedValue), Ansi.green).pipe(Doc.render({ + style: "pretty" + })) + + expect(lines).toEqual( + expect.arrayContaining([ + expect.stringContaining( + uncommittedValue + ), + expect.stringContaining( + committedValue + ) + ]) + ) + + expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(committedValue)) + ) + }).pipe(runEffect)) }) }) From 80e4bc055dfe54c26d34894191fb5d56b579f12e Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Tue, 27 Aug 2024 22:46:35 -0400 Subject: [PATCH 05/11] add test for entering a default value with tab --- packages/cli/test/Prompt.test.ts | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index 5bd9652295..4ad0abfbb6 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -98,6 +98,51 @@ describe("Prompt", () => { lines.findIndex((line) => line.includes(committedValue)) ) }).pipe(runEffect)) + + it("should accept the default value when the tab is pressed", () => + Effect.gen(function*() { + const prompt = Prompt.text({ + message: "Test Prompt", + default: "default-value" + }) + + const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ + name: "Test", + version: "1.0.0" + })) + + const fiber = yield* Effect.fork(cli([])) + + yield* MockTerminal.inputKey("tab") + yield* MockTerminal.inputKey("enter") + yield* Fiber.join(fiber) + + const lines = yield* MockConsole.getLines() + + const uncommittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + style: "pretty" + })) + + const enteredValue = Doc.annotate(Doc.text("default-value"), Ansi.combine(Ansi.underlined, Ansi.cyanBright)) + .pipe(Doc.render({ + style: "pretty" + })) + + expect(lines).toEqual( + expect.arrayContaining([ + expect.stringContaining( + uncommittedValue + ), + expect.stringContaining( + enteredValue + ) + ]) + ) + + expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(enteredValue)) + ) + }).pipe(runEffect)) }) describe("hidden", () => { From 50b8d62791116b31ab9c9e4102d47116c85cc7d9 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Tue, 27 Aug 2024 23:02:06 -0400 Subject: [PATCH 06/11] change commited to submitted in prompt test --- packages/cli/test/Prompt.test.ts | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index 4ad0abfbb6..09d09d2188 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -75,27 +75,27 @@ describe("Prompt", () => { const lines = yield* MockConsole.getLines() - const uncommittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + const unsubmittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ style: "pretty" })) - const committedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + const submittedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ style: "pretty" })) expect(lines).toEqual( expect.arrayContaining([ expect.stringContaining( - uncommittedValue + unsubmittedValue ), expect.stringContaining( - committedValue + submittedValue ) ]) ) - expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( - lines.findIndex((line) => line.includes(committedValue)) + expect(lines.findIndex((line) => line.includes(unsubmittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(submittedValue)) ) }).pipe(runEffect)) @@ -119,7 +119,7 @@ describe("Prompt", () => { const lines = yield* MockConsole.getLines() - const uncommittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + const unsubmittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ style: "pretty" })) @@ -131,7 +131,7 @@ describe("Prompt", () => { expect(lines).toEqual( expect.arrayContaining([ expect.stringContaining( - uncommittedValue + unsubmittedValue ), expect.stringContaining( enteredValue @@ -139,7 +139,7 @@ describe("Prompt", () => { ]) ) - expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( + expect(lines.findIndex((line) => line.includes(unsubmittedValue))).toBeLessThan( lines.findIndex((line) => line.includes(enteredValue)) ) }).pipe(runEffect)) @@ -249,27 +249,27 @@ describe("Prompt", () => { const lines = yield* MockConsole.getLines() - const uncommittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ + const unsubmittedValue = Doc.annotate(Doc.text("default-value"), Ansi.blackBright).pipe(Doc.render({ style: "pretty" })) - const committedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + const submittedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ style: "pretty" })) expect(lines).toEqual( expect.arrayContaining([ expect.stringContaining( - uncommittedValue + unsubmittedValue ), expect.stringContaining( - committedValue + submittedValue ) ]) ) - expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( - lines.findIndex((line) => line.includes(committedValue)) + expect(lines.findIndex((line) => line.includes(unsubmittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(submittedValue)) ) }).pipe(runEffect)) }) @@ -322,27 +322,27 @@ describe("Prompt", () => { const lines = yield* MockConsole.getLines() const redactedValue = Array.from("default-value", () => "*").join("") - const uncommittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.blackBright).pipe(Doc.render({ + const unsubmittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.blackBright).pipe(Doc.render({ style: "pretty" })) - const committedValue = Doc.annotate(Doc.text(redactedValue), Ansi.green).pipe(Doc.render({ + const submittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.green).pipe(Doc.render({ style: "pretty" })) expect(lines).toEqual( expect.arrayContaining([ expect.stringContaining( - uncommittedValue + unsubmittedValue ), expect.stringContaining( - committedValue + submittedValue ) ]) ) - expect(lines.findIndex((line) => line.includes(uncommittedValue))).toBeLessThan( - lines.findIndex((line) => line.includes(committedValue)) + expect(lines.findIndex((line) => line.includes(unsubmittedValue))).toBeLessThan( + lines.findIndex((line) => line.includes(submittedValue)) ) }).pipe(runEffect)) }) From fc9276773bda70d2631fe4bec6d018e0d399da41 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Tue, 27 Aug 2024 23:15:03 -0400 Subject: [PATCH 07/11] string repeat instead of array from in prompt test --- packages/cli/test/Prompt.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index 09d09d2188..a5756822ab 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -321,7 +321,7 @@ describe("Prompt", () => { const lines = yield* MockConsole.getLines() - const redactedValue = Array.from("default-value", () => "*").join("") + const redactedValue = "*".repeat("default-value".length) const unsubmittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.blackBright).pipe(Doc.render({ style: "pretty" })) From 4393eab90f1d0324c06ccc8b525d42cfcd14003f Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Wed, 28 Aug 2024 09:54:26 -0400 Subject: [PATCH 08/11] include patch changeset for cli default value render --- .changeset/late-apricots-thank.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/late-apricots-thank.md diff --git a/.changeset/late-apricots-thank.md b/.changeset/late-apricots-thank.md new file mode 100644 index 0000000000..584b10f222 --- /dev/null +++ b/.changeset/late-apricots-thank.md @@ -0,0 +1,9 @@ +--- +"@effect/cli": patch +--- + +Renders the default for all `Prompt` types that accepts `TextOptions`. + +- The default value will be rendered as ghost text for `Prompt.text` and `Prompt.list`. +- The default value will be rendered as redacted ghost text for `Prompt.password`. +- The default value will remain hidden for `Prompt.hidden`. From a65d636f9212b0e20623d8dd3bd1cdc86711def3 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Wed, 28 Aug 2024 19:24:57 -0400 Subject: [PATCH 09/11] remove cli apps in prompt tests --- packages/cli/test/Prompt.test.ts | 36 +++++--------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index a5756822ab..35e6949b35 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -1,5 +1,4 @@ import type * as CliApp from "@effect/cli/CliApp" -import * as Command from "@effect/cli/Command" import * as Prompt from "@effect/cli/Prompt" import * as MockConsole from "@effect/cli/test/services/MockConsole" import * as MockTerminal from "@effect/cli/test/services/MockTerminal" @@ -63,12 +62,7 @@ describe("Prompt", () => { default: "default-value" }) - const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ - name: "Test", - version: "1.0.0" - })) - - const fiber = yield* Effect.fork(cli([])) + const fiber = yield* Effect.fork(prompt) yield* MockTerminal.inputKey("enter") yield* Fiber.join(fiber) @@ -106,12 +100,7 @@ describe("Prompt", () => { default: "default-value" }) - const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ - name: "Test", - version: "1.0.0" - })) - - const fiber = yield* Effect.fork(cli([])) + const fiber = yield* Effect.fork(prompt) yield* MockTerminal.inputKey("tab") yield* MockTerminal.inputKey("enter") @@ -180,12 +169,7 @@ describe("Prompt", () => { default: "default-value" }) - const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ - name: "Test", - version: "1.0.0" - })) - - const fiber = yield* Effect.fork(cli([])) + const fiber = yield* Effect.fork(prompt) yield* MockTerminal.inputKey("enter") yield* Fiber.join(fiber) @@ -237,12 +221,7 @@ describe("Prompt", () => { default: "default-value" }) - const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ - name: "Test", - version: "1.0.0" - })) - - const fiber = yield* Effect.fork(cli([])) + const fiber = yield* Effect.fork(prompt) yield* MockTerminal.inputKey("enter") yield* Fiber.join(fiber) @@ -309,12 +288,7 @@ describe("Prompt", () => { default: "default-value" }) - const cli = Command.prompt("test-command", prompt, () => Effect.void).pipe(Command.run({ - name: "Test", - version: "1.0.0" - })) - - const fiber = yield* Effect.fork(cli([])) + const fiber = yield* Effect.fork(prompt) yield* MockTerminal.inputKey("enter") yield* Fiber.join(fiber) From f0df28e64dc17cc190d2953f25ad49022f1cc932 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Wed, 28 Aug 2024 19:29:12 -0400 Subject: [PATCH 10/11] remove use of match and render white on text prompts --- packages/cli/src/internal/prompt/text.ts | 23 +++++++++++------------ packages/cli/test/Prompt.test.ts | 6 +++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/internal/prompt/text.ts b/packages/cli/src/internal/prompt/text.ts index 7549019ca2..a78867d85c 100644 --- a/packages/cli/src/internal/prompt/text.ts +++ b/packages/cli/src/internal/prompt/text.ts @@ -4,7 +4,6 @@ import * as Doc from "@effect/printer-ansi/AnsiDoc" import * as Optimize from "@effect/printer/Optimize" import * as Arr from "effect/Array" import * as Effect from "effect/Effect" -import * as Match from "effect/Match" import * as Option from "effect/Option" import * as Redacted from "effect/Redacted" import type * as Prompt from "../../Prompt.js" @@ -66,17 +65,17 @@ function renderInput(nextState: State, options: Options, submitted: boolean) { const text = getValue(nextState, options) const annotation = Option.match(nextState.error, { - onNone: () => - Match.value(submitted) - .pipe( - Match.when(true, () => Ansi.green), - Match.orElse(() => - Match.value(nextState.value).pipe( - Match.when("", () => Ansi.blackBright), - Match.orElse(() => Ansi.combine(Ansi.underlined, Ansi.cyanBright)) - ) - ) - ), + onNone: () => { + if (submitted) { + return Ansi.white + } + + if (nextState.value.length === 0) { + return Ansi.blackBright + } + + return Ansi.combine(Ansi.underlined, Ansi.cyanBright) + }, onSome: () => Ansi.red }) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index 35e6949b35..f3c86e9b39 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -73,7 +73,7 @@ describe("Prompt", () => { style: "pretty" })) - const submittedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + const submittedValue = Doc.annotate(Doc.text("default-value"), Ansi.white).pipe(Doc.render({ style: "pretty" })) @@ -232,7 +232,7 @@ describe("Prompt", () => { style: "pretty" })) - const submittedValue = Doc.annotate(Doc.text("default-value"), Ansi.green).pipe(Doc.render({ + const submittedValue = Doc.annotate(Doc.text("default-value"), Ansi.white).pipe(Doc.render({ style: "pretty" })) @@ -300,7 +300,7 @@ describe("Prompt", () => { style: "pretty" })) - const submittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.green).pipe(Doc.render({ + const submittedValue = Doc.annotate(Doc.text(redactedValue), Ansi.white).pipe(Doc.render({ style: "pretty" })) From 61c3ae898d73a397834b0b8f24be32f6b6690f33 Mon Sep 17 00:00:00 2001 From: Christopher Dierkens Date: Wed, 28 Aug 2024 20:14:26 -0400 Subject: [PATCH 11/11] Update packages/cli/test/Prompt.test.ts Co-authored-by: Maxwell Brown --- packages/cli/test/Prompt.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/test/Prompt.test.ts b/packages/cli/test/Prompt.test.ts index f3c86e9b39..cf31988691 100644 --- a/packages/cli/test/Prompt.test.ts +++ b/packages/cli/test/Prompt.test.ts @@ -12,8 +12,8 @@ import * as Layer from "effect/Layer" import * as Redacted from "effect/Redacted" import { describe, expect, it } from "vitest" -const MainLive = Effect.gen(function*(_) { - const console = yield* _(MockConsole.make) +const MainLive = Effect.gen(function*() { + const console = yield* MockConsole.make return Layer.mergeAll( Console.setConsole(console), NodeFileSystem.layer,