diff --git a/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.test.tsx b/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.test.tsx index 63e58be7d4..08aec0def0 100644 --- a/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.test.tsx +++ b/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.test.tsx @@ -34,6 +34,46 @@ describe("BasePowerField", () => { expect(screen.queryByRole("combobox")).not.toBeInTheDocument(); }); + it("correctly renders a IP address field type", () => { + const field = factory.powerField({ field_type: PowerFieldType.IP_ADDRESS }); + render( + + + + ); + expect(screen.getByRole("textbox")).toBeInTheDocument(); + expect(screen.getByRole("textbox")).not.toHaveAttribute("type", "password"); + expect(screen.queryByRole("combobox")).not.toBeInTheDocument(); + }); + + it("correctly renders a Virsh address field type", () => { + const field = factory.powerField({ + field_type: PowerFieldType.VIRSH_ADDRESS, + }); + render( + + + + ); + expect(screen.getByRole("textbox")).toBeInTheDocument(); + expect(screen.getByRole("textbox")).not.toHaveAttribute("type", "password"); + expect(screen.queryByRole("combobox")).not.toBeInTheDocument(); + }); + + it("correctly renders a LXD address field type", () => { + const field = factory.powerField({ + field_type: PowerFieldType.LXD_ADDRESS, + }); + render( + + + + ); + expect(screen.getByRole("textbox")).toBeInTheDocument(); + expect(screen.getByRole("textbox")).not.toHaveAttribute("type", "password"); + expect(screen.queryByRole("combobox")).not.toBeInTheDocument(); + }); + it("correctly renders a password field type", () => { const field = factory.powerField({ field_type: PowerFieldType.PASSWORD, diff --git a/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.tsx b/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.tsx index 6c5b4fc435..067f2dca68 100644 --- a/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.tsx +++ b/src/app/base/components/PowerTypeFields/BasePowerField/BasePowerField.tsx @@ -70,7 +70,11 @@ export const BasePowerField = ({ } required={required} type={ - (field_type === PowerFieldType.STRING && "text") || + ((field_type === PowerFieldType.STRING || + field_type === PowerFieldType.IP_ADDRESS || + field_type === PowerFieldType.VIRSH_ADDRESS || + field_type === PowerFieldType.LXD_ADDRESS) && + "text") || (field_type === PowerFieldType.PASSWORD && "password") || undefined } diff --git a/src/app/store/general/types/enum.ts b/src/app/store/general/types/enum.ts index 408f39c5dd..48f91894f7 100644 --- a/src/app/store/general/types/enum.ts +++ b/src/app/store/general/types/enum.ts @@ -46,4 +46,7 @@ export enum PowerFieldType { MULTIPLE_CHOICE = "multiple_choice", PASSWORD = "password", STRING = "string", + IP_ADDRESS = "ip_address", + VIRSH_ADDRESS = "virsh_address", + LXD_ADDRESS = "lxd_address", } diff --git a/src/app/store/general/utils/powerTypes.ts b/src/app/store/general/utils/powerTypes.ts index 4d08914425..06409386cd 100644 --- a/src/app/store/general/utils/powerTypes.ts +++ b/src/app/store/general/utils/powerTypes.ts @@ -1,3 +1,4 @@ +import { isIP } from "is-ip"; import * as Yup from "yup"; import type { ObjectShape } from "yup/lib/object"; @@ -50,6 +51,23 @@ export const formatPowerParameters = ( return params; }, {}) || {}; +const getPowerFieldSchema = (fieldType: PowerFieldType) => { + switch (fieldType) { + case PowerFieldType.MULTIPLE_CHOICE: + return Yup.array().of(Yup.string()); + case PowerFieldType.IP_ADDRESS: + case PowerFieldType.VIRSH_ADDRESS: + case PowerFieldType.LXD_ADDRESS: + return Yup.string().test({ + name: "is-ip-address", + message: "Please enter a valid IP address.", + test: (value) => isIP(value as string), + }); + default: + return Yup.string(); + } +}; + /** * Generates a Yup validation object shape for power parameters based on the * selected power type and the field scopes. @@ -63,10 +81,7 @@ export const generatePowerParametersSchema = ( ): ObjectShape => powerType?.fields?.reduce((schema, field) => { if (fieldScopes.includes(field.scope)) { - let fieldSchema = - field.field_type === PowerFieldType.MULTIPLE_CHOICE - ? Yup.array().of(Yup.string()) - : Yup.string(); + let fieldSchema = getPowerFieldSchema(field.field_type); if (field.required) { fieldSchema = fieldSchema.required(`${field.label} required`); } diff --git a/yarn.lock b/yarn.lock index 56fa96cca9..f5a44a2bae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12976,7 +12976,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12994,15 +12994,6 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -13068,7 +13059,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -13082,13 +13073,6 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -14332,7 +14316,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -14350,15 +14334,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"