From 39fc99da5555ce1d0889c8d2fea627af6473cacd Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Wed, 9 Mar 2022 15:26:21 -0700 Subject: [PATCH 1/3] feat(testing): add test for optionDescriptions --- src/node/cli.ts | 2 +- test/unit/node/cli.test.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 503c9cf79a66..0418a144721e 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -124,7 +124,7 @@ type Options = { [P in keyof T]: Option> } -const options: Options> = { +export const options: Options> = { auth: { type: AuthType, description: "The type of authentication to use." }, password: { type: "string", diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index 47d02aece847..e3b5308e7995 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -13,6 +13,8 @@ import { shouldOpenInExistingInstance, splitOnFirstEquals, toVsCodeArgs, + optionDescriptions, + options, } from "../../../src/node/cli" import { shouldSpawnCliProcess } from "../../../src/node/main" import { generatePassword, paths } from "../../../src/node/util" @@ -753,3 +755,36 @@ describe("toVsCodeArgs", () => { }) }) }) + +describe("optionDescriptions", () => { + it("should return the descriptions of all the available options", () => { + const expectedOptionDescriptions = Object.entries(options) + .flat() + .filter((item: any) => { + if (item.description) { + return item.description + } + }) + .map((item: any) => item.description) + const actualOptionDescriptions = optionDescriptions() + // We need both the expected and the actual + // Both of these are string[] + // We then loop through the expectedOptionDescriptions + // and check that this expectedDescription exists in the + // actualOptionDescriptions + + // To do that we need to loop through actualOptionDescriptions + // and make sure we have a substring match + expectedOptionDescriptions.forEach((expectedDescription) => { + const exists = actualOptionDescriptions.find((desc) => { + if ( + desc.replace(/\n/g, " ").replace(/ /g, "").includes(expectedDescription.replace(/\n/g, " ").replace(/ /g, "")) + ) { + return true + } + return false + }) + expect(exists).toBeTruthy() + }) + }) +}) From 1282e8f5951230522e9d34e813c628823e2c9898 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 11 Mar 2022 11:55:35 -0700 Subject: [PATCH 2/3] refactor(cli): optional arg in optionDescriptions --- src/node/cli.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 0418a144721e..d3205fd56ec8 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -235,8 +235,8 @@ export const options: Options> = { }, } -export const optionDescriptions = (): string[] => { - const entries = Object.entries(options).filter(([, v]) => !!v.description) +export const optionDescriptions = (opts = options): string[] => { + const entries = Object.entries(opts).filter(([, v]) => !!v.description) const widths = entries.reduce( (prev, [k, v]) => ({ long: k.length > prev.long ? k.length : prev.long, From 1dccdc2f47a0dc1a45ca8d5ed9b2ad5c219e676f Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 11 Mar 2022 12:15:31 -0700 Subject: [PATCH 3/3] feat: add more tests for optionDescriptions --- src/node/cli.ts | 4 +-- test/unit/node/cli.test.ts | 64 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index d3205fd56ec8..2e638c8cf2de 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -120,7 +120,7 @@ type OptionType = T extends boolean ? "string[]" : "unknown" -type Options = { +export type Options = { [P in keyof T]: Option> } @@ -235,7 +235,7 @@ export const options: Options> = { }, } -export const optionDescriptions = (opts = options): string[] => { +export const optionDescriptions = (opts: Partial>> = options): string[] => { const entries = Object.entries(opts).filter(([, v]) => !!v.description) const widths = entries.reduce( (prev, [k, v]) => ({ diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index e3b5308e7995..73cbea17d4b8 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -15,6 +15,9 @@ import { toVsCodeArgs, optionDescriptions, options, + Options, + AuthType, + OptionalString, } from "../../../src/node/cli" import { shouldSpawnCliProcess } from "../../../src/node/main" import { generatePassword, paths } from "../../../src/node/util" @@ -787,4 +790,65 @@ describe("optionDescriptions", () => { expect(exists).toBeTruthy() }) }) + it("should visually align multiple options", () => { + const opts: Partial>> = { + "cert-key": { type: "string", path: true, description: "Path to certificate key when using non-generated cert." }, + "cert-host": { + type: "string", + description: "Hostname to use when generating a self signed certificate.", + }, + "disable-update-check": { + type: "boolean", + description: + "Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and \n" + + "then notifies you once every week that a new release is available.", + }, + } + expect(optionDescriptions(opts)).toStrictEqual([ + " --cert-key Path to certificate key when using non-generated cert.", + " --cert-host Hostname to use when generating a self signed certificate.", + ` --disable-update-check Disable update check. Without this flag, code-server checks every 6 hours against the latest github release and + then notifies you once every week that a new release is available.`, + ]) + }) + it("should add all valid options for enumerated types", () => { + const opts: Partial>> = { + auth: { type: AuthType, description: "The type of authentication to use." }, + } + expect(optionDescriptions(opts)).toStrictEqual([" --auth The type of authentication to use. [password, none]"]) + }) + + it("should show if an option is deprecated", () => { + const opts: Partial>> = { + link: { + type: OptionalString, + description: ` + Securely bind code-server via our cloud service with the passed name. You'll get a URL like + https://hostname-username.coder.co at which you can easily access your code-server instance. + Authorization is done via GitHub. + `, + deprecated: true, + }, + } + expect(optionDescriptions(opts)).toStrictEqual([ + ` --link (deprecated) Securely bind code-server via our cloud service with the passed name. You'll get a URL like + https://hostname-username.coder.co at which you can easily access your code-server instance. + Authorization is done via GitHub.`, + ]) + }) + + it("should show newlines in description", () => { + const opts: Partial>> = { + "install-extension": { + type: "string[]", + description: + "Install or update a VS Code extension by id or vsix. The identifier of an extension is `${publisher}.${name}`.\n" + + "To install a specific version provide `@${version}`. For example: 'vscode.csharp@1.2.3'.", + }, + } + expect(optionDescriptions(opts)).toStrictEqual([ + ` --install-extension Install or update a VS Code extension by id or vsix. The identifier of an extension is \`\${publisher}.\${name}\`. + To install a specific version provide \`@\${version}\`. For example: 'vscode.csharp@1.2.3'.`, + ]) + }) })