From 572b6f20f7a8f31d89d0a198d41fc86c9738457e Mon Sep 17 00:00:00 2001 From: Peter Makowski Date: Wed, 19 Jun 2024 11:42:29 +0200 Subject: [PATCH] fix(settings): fix list spacing #5464 (#5470) - Update the parsing logic to correctly split the values and remove any leading or trailing spaces --- .../RepositoryForm/RepositoryForm.test.tsx | 48 +++++++++++++++++++ .../RepositoryForm/RepositoryForm.tsx | 26 ++++++---- src/app/utils/index.ts | 1 + .../utils/parseCommaSeparatedValues.test.ts | 39 +++++++++++++++ src/app/utils/parseCommaSeparatedValues.ts | 6 +++ 5 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 src/app/utils/parseCommaSeparatedValues.test.ts create mode 100644 src/app/utils/parseCommaSeparatedValues.ts diff --git a/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.test.tsx b/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.test.tsx index 7ed4129d5a..e9497112d4 100644 --- a/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.test.tsx +++ b/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.test.tsx @@ -338,4 +338,52 @@ describe("RepositoryForm", () => { ).toBe(true); expect(actions.some((action) => action.type === "message/add")).toBe(true); }); + + it("correctly parses comma-separated distributions and components", async () => { + const store = mockStore(state); + render( + + + + + + ); + + await userEvent.type( + screen.getByRole("textbox", { name: RepositoryFormLabels.Name }), + "name" + ); + await userEvent.type( + screen.getByRole("textbox", { name: RepositoryFormLabels.URL }), + "http://www.website.com" + ); + await userEvent.type( + screen.getByRole("textbox", { name: RepositoryFormLabels.Distributions }), + "focal, jammy, noble" + ); + await userEvent.type( + screen.getByRole("textbox", { name: RepositoryFormLabels.Components }), + "main, universe, restricted" + ); + + await userEvent.click( + screen.getByRole("button", { name: "Save repository" }) + ); + + const createAction = store + .getActions() + .find((action) => action.type === "packagerepository/create"); + expect(createAction.payload.params.distributions).toEqual([ + "focal", + "jammy", + "noble", + ]); + expect(createAction.payload.params.components).toEqual([ + "main", + "universe", + "restricted", + ]); + }); }); diff --git a/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.tsx b/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.tsx index 184f49157b..7509b8d134 100644 --- a/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.tsx +++ b/src/app/settings/views/Repositories/RepositoryForm/RepositoryForm.tsx @@ -25,20 +25,30 @@ import type { PackageRepository, } from "@/app/store/packagerepository/types"; import { getRepoDisplayName } from "@/app/store/packagerepository/utils"; +import { parseCommaSeparatedValues } from "@/app/utils"; type Props = { repository?: PackageRepository | null; type: "ppa" | "repository"; }; +const commaSeparated = Yup.string() + .transform((value) => + value + .split(",") + .map((s: string) => s.trim()) + .join(", ") + ) + .matches(/^(?:[^,\s]+(?:,\s*[^,\s]+)*)?$/, "Must be comma-separated."); + const RepositorySchema = Yup.object().shape({ arches: Yup.array(), - components: Yup.string(), + components: commaSeparated, default: Yup.boolean().required(), disable_sources: Yup.boolean().required(), disabled_components: Yup.array(), disabled_pockets: Yup.array(), - distributions: Yup.string(), + distributions: commaSeparated, enabled: Yup.boolean().required(), key: Yup.string(), name: Yup.string().required("Name field required."), @@ -150,12 +160,12 @@ export const RepositoryForm = ({ type, repository }: Props): JSX.Element => { params.disabled_components = values.disabled_components; params.disabled_pockets = values.disabled_pockets; } else { - params.components = values.components - .split(" ,") - .filter(Boolean); - params.distributions = values.distributions - .split(" ,") - .filter(Boolean); + params.components = parseCommaSeparatedValues( + values.components + ); + params.distributions = parseCommaSeparatedValues( + values.distributions + ); params.enabled = values.enabled; } diff --git a/src/app/utils/index.ts b/src/app/utils/index.ts index 9408e2c8e0..f40bb889b9 100644 --- a/src/app/utils/index.ts +++ b/src/app/utils/index.ts @@ -35,3 +35,4 @@ export { timeSpanToSeconds, timeSpanToMinutes, } from "./timeSpan"; +export { parseCommaSeparatedValues } from "./parseCommaSeparatedValues"; diff --git a/src/app/utils/parseCommaSeparatedValues.test.ts b/src/app/utils/parseCommaSeparatedValues.test.ts new file mode 100644 index 0000000000..f08f686563 --- /dev/null +++ b/src/app/utils/parseCommaSeparatedValues.test.ts @@ -0,0 +1,39 @@ +import { parseCommaSeparatedValues } from "./parseCommaSeparatedValues"; + +describe("parseCommaSeparatedValues", () => { + it("should correctly parse a single value with no spaces", () => { + expect(parseCommaSeparatedValues("value")).toEqual(["value"]); + }); + + it("should correctly parse multiple values with spaces", () => { + expect(parseCommaSeparatedValues("apple, banana, cherry")).toEqual([ + "apple", + "banana", + "cherry", + ]); + }); + + it("should handle leading and trailing spaces", () => { + expect(parseCommaSeparatedValues(" apple, banana , cherry ")).toEqual([ + "apple", + "banana", + "cherry", + ]); + }); + + it("should ignore empty strings between commas", () => { + expect(parseCommaSeparatedValues("apple,,banana, ,cherry")).toEqual([ + "apple", + "banana", + "cherry", + ]); + }); + + it("should return an empty array if the input is only commas or spaces", () => { + expect(parseCommaSeparatedValues(" , , , ")).toEqual([]); + }); + + it("should return an empty array if the input is an empty string", () => { + expect(parseCommaSeparatedValues("")).toEqual([]); + }); +}); diff --git a/src/app/utils/parseCommaSeparatedValues.ts b/src/app/utils/parseCommaSeparatedValues.ts new file mode 100644 index 0000000000..563265b6db --- /dev/null +++ b/src/app/utils/parseCommaSeparatedValues.ts @@ -0,0 +1,6 @@ +export const parseCommaSeparatedValues = (input: string): string[] => { + return input + .split(",") + .map((s) => s.trim()) + .filter(Boolean); +};