Skip to content
Open
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
51 changes: 51 additions & 0 deletions client/src/app/hooks/useFormChangeHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useEffect, useMemo } from "react";
import {
FieldPathValues,
FieldValues,
Path,
UseFormReturn,
useWatch,
} from "react-hook-form";

/**
* Generic form change handler that watches specific form fields and calls a state change callback.
* @template TFormValues - The type of the form values
* @template TState - The type of the state object
* @template TWatchFields - The tuple type of field paths being watched
*/
export const useFormChangeHandler = <
TFormValues extends FieldValues,
TState,
TWatchFields extends Path<TFormValues>[],
>({
form,
onStateChanged,
watchFields,
mapValuesToState,
}: {
form: UseFormReturn<TFormValues>;
onStateChanged: (state: TState) => void;
watchFields: TWatchFields;
mapValuesToState: (
values: FieldPathValues<TFormValues, TWatchFields>,
isFormValid: boolean
) => TState;
}) => {
const {
control,
formState: { isValid },
} = form;

const watchedValues = useWatch<TFormValues>({
control,
name: [...watchFields] as Path<TFormValues>[],
}) as FieldPathValues<TFormValues, TWatchFields>;

const state = useMemo((): TState => {
return mapValuesToState(watchedValues, isValid);
}, [mapValuesToState, watchedValues, isValid]);

useEffect(() => {
onStateChanged(state);
}, [onStateChanged, state]);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Target, TargetLabel } from "@app/api/models";
import { Application, Target, TargetLabel } from "@app/api/models";

import { updateSelectedTargetLabels } from "../utils";
import { isModeSupported, updateSelectedTargetLabels } from "../utils";

const TARGET_LABELS: TargetLabel[] = [
{
Expand Down Expand Up @@ -56,6 +56,140 @@ const TARGET_B: Target = {
],
};

describe("analysis-wizard utils - isModeSupported()", () => {
it("should return true for binary-upload mode regardless of application state", () => {
const application: Application = {
id: 1,
name: "Test App",
migrationWave: null,
};
expect(isModeSupported(application, "binary-upload")).toBe(true);
});

it("should return true for binary mode when application has valid binary format", () => {
const application: Application = {
id: 1,
name: "Test App",
binary: "group:artifact:version",
migrationWave: null,
};
expect(isModeSupported(application, "binary")).toBe(true);
});

it("should return false for binary mode when application binary is missing", () => {
const application: Application = {
id: 1,
name: "Test App",
migrationWave: null,
};
expect(isModeSupported(application, "binary")).toBe(false);
});

it("should return false for binary mode when application binary is empty", () => {
const application: Application = {
id: 1,
name: "Test App",
binary: "",
migrationWave: null,
};
expect(isModeSupported(application, "binary")).toBe(false);
});

it("should return false for binary mode when application binary does not match pattern", () => {
const application: Application = {
id: 1,
name: "Test App",
binary: "invalid-format",
migrationWave: null,
};
expect(isModeSupported(application, "binary")).toBe(false);
});

it("should return true for source-code-deps mode when repository URL exists", () => {
const application: Application = {
id: 1,
name: "Test App",
repository: {
url: "https://github.com/user/repo.git",
},
migrationWave: null,
};
expect(isModeSupported(application, "source-code-deps")).toBe(true);
});

it("should return false for source-code-deps mode when repository URL is missing", () => {
const application: Application = {
id: 1,
name: "Test App",
migrationWave: null,
};
expect(isModeSupported(application, "source-code-deps")).toBe(false);
});

it("should return false for source-code-deps mode when repository URL is empty", () => {
const application: Application = {
id: 1,
name: "Test App",
repository: {
url: "",
},
migrationWave: null,
};
expect(isModeSupported(application, "source-code-deps")).toBe(false);
});

it("should return true for source-code mode when repository URL exists", () => {
const application: Application = {
id: 1,
name: "Test App",
repository: {
url: "https://github.com/user/repo.git",
},
migrationWave: null,
};
expect(isModeSupported(application, "source-code")).toBe(true);
});

it("should return false for source-code mode when repository URL is missing", () => {
const application: Application = {
id: 1,
name: "Test App",
migrationWave: null,
};
expect(isModeSupported(application, "source-code")).toBe(false);
});

it("should return false for source-code mode when repository URL is empty", () => {
const application: Application = {
id: 1,
name: "Test App",
repository: {
url: "",
},
migrationWave: null,
};
expect(isModeSupported(application, "source-code")).toBe(false);
});

it("should return false for undefined mode", () => {
const application: Application = {
id: 1,
name: "Test App",
migrationWave: null,
};
expect(isModeSupported(application, undefined)).toBe(false);
});

it("should return false for unknown mode", () => {
const application: Application = {
id: 1,
name: "Test App",
migrationWave: null,
};
expect(isModeSupported(application, "unknown-mode")).toBe(false);
});
});

describe("analysis-wizard utils - updateSelectedTargetLabels()", () => {
it("add a label to an empty list", () => {
const theResult = updateSelectedTargetLabels(
Expand Down
Loading
Loading