Skip to content

fix(site): validate group name before submitting to the backend #16115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ site/stats/

# direnv
.envrc
.direnv
*.test

# Loadtesting
Expand Down
11 changes: 11 additions & 0 deletions site/src/pages/GroupsPage/CreateGroupPageView.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import { mockApiError } from "testHelpers/entities";
import { CreateGroupPageView } from "./CreateGroupPageView";

Expand All @@ -21,3 +22,13 @@ export const WithError: Story = {
initialTouched: { name: true },
},
};

export const InvalidName: Story = {
play: async ({ canvasElement }) => {
const user = userEvent.setup();
const body = within(canvasElement.ownerDocument.body);
const input = await body.findByLabelText("Name");
await user.type(input, "$om3 !nv@lid Name");
input.blur();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BrunoQuaresma I'm not sure if I'm missing something, but are these tests incomplete?

We're checking that the interactive elements exist and that the user can fill them out incorrectly, but then it looks like we're not asserting that anything happens in response to that? I would expect something like asserting that some kind of validation message appears

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chromatic will snapshot the final state, which will include the error message

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that's good to know. I guess my worry is that without an expect-style assertion, somebody could accidentally accept the stories and miss that the validation message is no longer on screen

I'd prefer to force the test to fail if there's a good chance something is wrong, but maybe I subscribe too much to paranoia-driven development

},
};
11 changes: 9 additions & 2 deletions site/src/pages/GroupsPage/CreateGroupPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import { Stack } from "components/Stack/Stack";
import { type FormikTouched, useFormik } from "formik";
import type { FC } from "react";
import { useNavigate } from "react-router-dom";
import { getFormHelpers, onChangeTrimmed } from "utils/formUtils";
import {
getFormHelpers,
nameValidator,
onChangeTrimmed,
} from "utils/formUtils";
import * as Yup from "yup";

const validationSchema = Yup.object({
name: Yup.string().required().label("Name"),
name: nameValidator("Name"),
});

export type CreateGroupPageViewProps = {
Expand Down Expand Up @@ -62,13 +66,16 @@ export const CreateGroupPageView: FC<CreateGroupPageViewProps> = ({
autoFocus
fullWidth
label="Name"
onChange={onChangeTrimmed(form)}
autoComplete="name"
/>
<TextField
{...getFieldHelpers("display_name", {
helperText: "Optional: keep empty to default to the name.",
})}
fullWidth
label="Display Name"
autoComplete="display_name"
/>
<IconField
{...getFieldHelpers("avatar_url")}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ export const WithError: Story = {
});
},
};

export const InvalidName: Story = {
play: async ({ canvasElement }) => {
const user = userEvent.setup();
const body = within(canvasElement.ownerDocument.body);
const input = await body.findByLabelText("Name");
await user.type(input, "$om3 !nv@lid Name");
input.blur();
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ import { Spinner } from "components/Spinner/Spinner";
import { useFormik } from "formik";
import type { FC } from "react";
import { useNavigate } from "react-router-dom";
import { getFormHelpers, onChangeTrimmed } from "utils/formUtils";
import {
getFormHelpers,
nameValidator,
onChangeTrimmed,
} from "utils/formUtils";
import * as Yup from "yup";

const validationSchema = Yup.object({
name: Yup.string().required().label("Name"),
name: nameValidator("Name"),
});

export type CreateGroupPageViewProps = {
Expand Down Expand Up @@ -69,13 +73,16 @@ export const CreateGroupPageView: FC<CreateGroupPageViewProps> = ({
autoFocus
fullWidth
label="Name"
onChange={onChangeTrimmed(form)}
autoComplete="name"
/>
<TextField
{...getFieldHelpers("display_name", {
helperText: "Optional: keep empty to default to the name.",
})}
fullWidth
label="Display Name"
autoComplete="display_name"
/>
<IconField
{...getFieldHelpers("avatar_url")}
Expand Down
Loading