-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* feat: Add app directory and restructure related files. * feat: Change unused vars to error for linting. * feat: Add Form component based on aiven-core implementation. * feat: Add userEvent package. @testing-library/user-event has a peerDep for @testing-library/dom but it's a different version than the one that @testing-library/react uses and brings in as a devDep. Solution based on research on GitHub shows we can either ignore the warning in pnpm or install the @testing-library/dom as devDep in the version that @testing-library/react uses it. I did the latter hoping that in one of the next versions of @testing-library/user-event this will be resolved. see testing-library/user-event#438 (comment) testing-library/user-event#551 (comment) * feat: Add tests for Form based on aiven-core. * feat: Introduce explicit typing for Form. - remove use of 'any' type - add stricter rule for use of any for linting * feat: Update lodash usage to match dependencies. * Remove comment about aiven-core mirroring * Remove unused library. * Rename and restructure pages files. - `index.tsx` should be reserved for resource root - files for pages can live on first level in `pages` - rename files to match naming conventions for react - update documentation to be more explicit and correct * Add login page to routing. * Reorder lint rules after rebase.
- Loading branch information
1 parent
bf9bb65
commit 57eb19c
Showing
17 changed files
with
460 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import { Button } from "@aivenio/design-system"; | ||
import type { RenderResult } from "@testing-library/react"; | ||
import { cleanup, render, screen, waitFor } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import React from "react"; | ||
import type { DeepPartial, FieldValues } from "react-hook-form"; | ||
import { | ||
Form, | ||
SubmitErrorHandler, | ||
SubmitHandler, | ||
TextInput, | ||
useForm, | ||
} from "src/app/components/Form"; | ||
import { z, ZodSchema } from "zod"; | ||
|
||
type WrapperProps<T extends FieldValues> = { | ||
schema: ZodSchema; | ||
defaultValues?: DeepPartial<T>; | ||
onSubmit: SubmitHandler<T>; | ||
onError: SubmitErrorHandler<T>; | ||
}; | ||
|
||
const Wrapper = <T extends FieldValues>({ | ||
schema, | ||
defaultValues, | ||
onSubmit, | ||
onError, | ||
children, | ||
}: React.PropsWithChildren<WrapperProps<T>>): React.ReactElement => { | ||
const form = useForm<T>({ schema, defaultValues }); | ||
return ( | ||
<Form onSubmit={onSubmit} onError={onError} {...form}> | ||
{children} | ||
</Form> | ||
); | ||
}; | ||
|
||
describe("Form", () => { | ||
const onSubmit = jest.fn(); | ||
const onError = jest.fn(); | ||
let results: RenderResult; | ||
let user: ReturnType<typeof userEvent.setup>; | ||
|
||
beforeEach(() => { | ||
user = userEvent.setup(); | ||
}); | ||
|
||
afterEach(() => { | ||
cleanup(); | ||
onSubmit.mockClear(); | ||
onError.mockClear(); | ||
}); | ||
|
||
const renderForm = <T extends FieldValues>( | ||
children: React.ReactNode, | ||
{ | ||
schema, | ||
defaultValues, | ||
}: { schema: ZodSchema; defaultValues?: DeepPartial<T> } | ||
) => { | ||
return render( | ||
<Wrapper<T> | ||
schema={schema} | ||
defaultValues={defaultValues} | ||
onSubmit={onSubmit} | ||
onError={onError} | ||
> | ||
{children} | ||
<Button type="submit" title="Submit" /> | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
const typeText = async (value: string) => { | ||
const el = screen.getByRole("textbox"); | ||
await user.clear(el); | ||
await user.type(el, value); | ||
}; | ||
|
||
const submit = async () => { | ||
await user.click(screen.getByRole("button", { name: "Submit" })); | ||
}; | ||
|
||
const assertSubmitted = (data: Record<string, string>) => { | ||
expect(onSubmit).toHaveBeenCalledWith(data, expect.anything()); | ||
}; | ||
|
||
describe("<Form>", () => { | ||
const schema = z.object({ name: z.string().min(3, "error") }); | ||
type Schema = z.infer<typeof schema>; | ||
|
||
beforeEach(() => { | ||
results = renderForm( | ||
<TextInput<Schema> name="name" labelText="TextInput" />, | ||
{ schema } | ||
); | ||
}); | ||
|
||
it("should call onSubmit() after submit if there are no validation errors", async () => { | ||
await user.type(screen.getByLabelText("TextInput"), "abc"); | ||
await submit(); | ||
await waitFor(() => | ||
expect(onSubmit).toHaveBeenCalledWith( | ||
{ name: "abc" }, | ||
expect.anything() | ||
) | ||
); | ||
}); | ||
|
||
it("should call onError() after submit if there are validation errors", async () => { | ||
await user.type(screen.getByLabelText("TextInput"), "a"); | ||
await submit(); | ||
await waitFor(() => expect(onError).toHaveBeenCalled()); | ||
expect(onError.mock.calls[0][0]).toMatchObject({ | ||
name: { message: "error" }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe("<TextInput>", () => { | ||
const schema = z.object({ name: z.string().min(3, "error") }); | ||
type Schema = z.infer<typeof schema>; | ||
|
||
beforeEach(() => { | ||
results = renderForm( | ||
<TextInput<Schema> name="name" labelText="TextInput" />, | ||
{ schema } | ||
); | ||
}); | ||
|
||
it("should render <TextInput>", () => { | ||
expect(results.container).toMatchSnapshot(); | ||
}); | ||
|
||
it("should render label", () => { | ||
expect(screen.queryByLabelText("TextInput")).toBeVisible(); | ||
}); | ||
|
||
it("should sync value to form state", async () => { | ||
await typeText("value{tab}"); | ||
await submit(); | ||
assertSubmitted({ name: "value" }); | ||
}); | ||
|
||
it("should render errors after blur event and hide them after valid input", async () => { | ||
await typeText("a{tab}"); | ||
await waitFor(() => expect(screen.queryByText("error")).toBeVisible()); | ||
|
||
await typeText("abc{tab}"); | ||
await waitFor(() => expect(screen.queryByText("error")).toBeNull()); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.