diff --git a/app/nn-assign/page.tsx b/app/nn-assign/page.tsx index 4d6440d..cdfaea2 100644 --- a/app/nn-assign/page.tsx +++ b/app/nn-assign/page.tsx @@ -2,7 +2,12 @@ import { Footer } from "@/components/Footer/Footer"; import { Header } from "@/components/Header/Header"; import { NNAssignForm } from "@/components/NNAssignForm/NNAssignForm"; -export default async function Join() { +export const metadata = { + title: "Assign a Network Number", + description: "Assign a Network Number to an existing Install", +}; + +export default async function NNAssign() { return ( <>
diff --git a/components/NNAssignForm/NNAssignForm.module.scss b/components/NNAssignForm/NNAssignForm.module.scss index b791d50..57c67df 100644 --- a/components/NNAssignForm/NNAssignForm.module.scss +++ b/components/NNAssignForm/NNAssignForm.module.scss @@ -89,3 +89,9 @@ display: flex; justify-content: center; } + +.alert { + display: flex; + justify-content: center; + align-items: center; +} diff --git a/components/NNAssignForm/NNAssignForm.tsx b/components/NNAssignForm/NNAssignForm.tsx index faafcc2..8f76458 100644 --- a/components/NNAssignForm/NNAssignForm.tsx +++ b/components/NNAssignForm/NNAssignForm.tsx @@ -1,71 +1,66 @@ "use client"; +import React, { useState } from "react"; import Button from "@mui/material/Button"; -import { NNAssignFormInput } from "@/app/io"; -import { submitNNAssignForm } from "@/app/api"; -import { useRouter } from "next/navigation"; +import { useForm, SubmitHandler } from "react-hook-form"; +import styles from "./NNAssignForm.module.scss"; +import { Alert } from "@mui/material"; +import { getMeshDBAPIEndpoint } from "@/app/endpoint"; import { ToastContainer, toast } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; -import { toastErrorMessage } from "@/app/utils/toastErrorMessage"; - -import styles from "./NNAssignForm.module.scss"; +type NNAssignRequestValues = { + install_number: string; + password: string; +}; -import { useState } from "react"; +export type { NNAssignRequestValues }; export function NNAssignForm() { - function parseForm(event: FormData) { - const data: Record = {}; - event.forEach((value, key) => { - if (key === "install_number") { - data[key] = Number(value); - } else { - data[key] = value; - } - }); + const { + register, + handleSubmit, + formState: { isDirty, isValid }, + } = useForm(); - return NNAssignFormInput.parse(data); - } + const [isSubmitted, setIsSubmitted] = useState(false); + const [networkNumber, setNetworkNumber] = useState(""); + const [nnMessage, setNnMessage] = useState(""); - async function sendForm(event: FormData) { - console.log(event); + async function submitNNAssignRequest(input: NNAssignRequestValues) { + setIsSubmitted(true); + return fetch(`${await getMeshDBAPIEndpoint()}/api/v1/nn-assign/`, { + method: "POST", + body: JSON.stringify(input), + }) + .then(async (response) => { + if (response.ok) { + const responseJson = await response.json(); + setNetworkNumber(responseJson.network_number); + setNnMessage(responseJson.detail); + return; + } - try { - setDisableSubmitButton(true); - let a: NNAssignFormInput = parseForm(event); - console.log(a); - let resp = await submitNNAssignForm(a); - if (resp.created) { - toast.success("Success! Network Number is: " + resp.network_number, { - hideProgressBar: true, - autoClose: false, - }); - } else { - toast.success("Found existing Network Number: " + resp.network_number, { - hideProgressBar: true, - autoClose: false, - }); - } - setNetworkNumber(resp.network_number); - } catch (e) { - console.log("Could not submit NNAssign Form: "); - console.log(e); - toastErrorMessage(e); - setDisableSubmitButton(false); - return; - } + throw response; + }) + .catch(async (error) => { + const errorJson = await error.json(); + const detail = await errorJson.detail; + console.error(`Could not assign NN: ${detail}`); + toast.error(`Could not assign NN: ${detail}`); + setIsSubmitted(false); + }); } - const initialState = {}; - const [value, setValue] = useState(); - const router = useRouter(); - const [disableSubmitButton, setDisableSubmitButton] = useState(false); - const [networkNumber, setNetworkNumber] = useState(-1); + const onSubmit: SubmitHandler = (data) => { + console.log(data); + submitNNAssignRequest(data); + }; return ( <>
-
+

Request Network Number

Enter an install number and the Pre-Shared Key, and receive a @@ -74,14 +69,18 @@ export function NNAssignForm() {

@@ -89,8 +88,7 @@ export function NNAssignForm() {
- -

- {networkNumber} -

- + +
+ +
+ ); } diff --git a/tests/mock/handlers.ts b/tests/mock/handlers.ts index aeebfe4..a5a7d98 100644 --- a/tests/mock/handlers.ts +++ b/tests/mock/handlers.ts @@ -2,6 +2,7 @@ import { http, HttpResponse } from "msw"; import { expectedAPIRequestData } from "../util"; import { isDeepStrictEqual } from "util"; import { JoinFormValues } from "@/components/JoinForm/JoinForm"; +import { NNAssignRequestValues } from "@/components/NNAssignForm/NNAssignForm"; export default [ http.post("/api/v1/join/", async ({ request }) => { @@ -116,4 +117,59 @@ export default [ return HttpResponse.json(json, { status: 201 }); }), + http.post("/api/v1/nn-assign/", async ({ request }) => { + console.debug("Hello from mocked NN Assign API."); + + const requestJson = await request.json(); + + if (requestJson === undefined || requestJson === null) { + return HttpResponse.json( + { detail: "Mock: Missing request body" }, + { status: 400 }, + ); + } + + const nnAssignRequest: NNAssignRequestValues = + requestJson as NNAssignRequestValues; + + // Firstly, check if we have the right password + if (nnAssignRequest.password != "localdev") { + console.debug("Mock bad password"); + return HttpResponse.json( + { detail: "Mock failure. Authentication Failed." }, + { status: 400 }, + ); + } + + if (nnAssignRequest.install_number == "20000") { + const json = { + detail: "Network Number has been assigned!", + building_id: 69, + install_id: 69, + install_number: 20000, + network_number: 420, + created: true, + }; + + return HttpResponse.json(json, { status: 201 }); + } + + if (nnAssignRequest.install_number == "30000") { + const message = `This Install Number (30000) already has a Network Number (520) associated with it!`; + const json = { + detail: message, + building_id: 79, + install_id: 79, + install_number: 30000, + network_number: 520, + created: false, + }; + return HttpResponse.json(json, { status: 200 }); + } + + return HttpResponse.json( + { detail: "Mock failure. Server Error." }, + { status: 500 }, + ); + }), ]; diff --git a/tests/nn_assign_form.spec.ts b/tests/nn_assign_form.spec.ts new file mode 100644 index 0000000..068f6f0 --- /dev/null +++ b/tests/nn_assign_form.spec.ts @@ -0,0 +1,90 @@ +import { test, expect } from "@/tests/mock/test"; + +import { + sampleData, + fillOutJoinForm, + submitSuccessExpected, + submitFailureExpected, + submitConfirmationDialogExpected, + sampleNJData, + submitAndCheckToast, + expectSuccess, +} from "@/tests/util"; + +const timeout = 10000; +const clickTimeout = 1000; + +// Unit tests for the Join Form. +// +// These tests will mock a connection to MeshDB. It is simply making sure that +// the form creates a good-looking payload and can hit a mock API. + +test("nn assign happy", async ({ page }) => { + test.setTimeout(timeout); + await page.goto("/nn-assign"); + + await expect(page).toHaveTitle(/Assign a Network Number/); + + await page.getByPlaceholder("Install Number").fill("20000"); + await page.getByPlaceholder("Pre-Shared Key").fill("localdev"); + + await page.getByRole("button", { name: /Submit/i }).click(); + await page.waitForTimeout(clickTimeout); + + await expect(page.locator("[id='alert-network-number']")).toBeVisible(); + await expect(page.locator("[id='assigned-network-number']")).toHaveText( + "420", + ); +}); + +test("nn assign already assigned", async ({ page }) => { + test.setTimeout(timeout); + await page.goto("/nn-assign"); + + await expect(page).toHaveTitle(/Assign a Network Number/); + + await page.getByPlaceholder("Install Number").fill("30000"); + await page.getByPlaceholder("Pre-Shared Key").fill("localdev"); + + await page.getByRole("button", { name: /Submit/i }).click(); + await page.waitForTimeout(clickTimeout); + + await expect(page.locator("[id='alert-network-number']")).toBeVisible(); + await expect(page.locator("[id='nn-message']")).toHaveText( + "This Install Number (30000) already has a Network Number (520) associated with it!", + ); + await expect(page.locator("[id='assigned-network-number']")).toHaveText( + "520", + ); +}); + +test("nn assign wrong password", async ({ page }) => { + test.setTimeout(timeout); + await page.goto("/nn-assign"); + + await expect(page).toHaveTitle(/Assign a Network Number/); + + await page.getByPlaceholder("Install Number").fill("20000"); + await page.getByPlaceholder("Pre-Shared Key").fill("badpassword"); + + await page.getByRole("button", { name: /Submit/i }).click(); + await page.waitForTimeout(clickTimeout); + + await expect(page.locator("[id='alert-network-number']")).toBeHidden(); + await expect(page.locator("[id='assigned-network-number']")).toHaveText(""); +}); + +test("nn assign no password", async ({ page }) => { + test.setTimeout(timeout); + await page.goto("/nn-assign"); + + await expect(page).toHaveTitle(/Assign a Network Number/); + + await page.getByPlaceholder("Install Number").fill("20000"); + + await expect(page.getByRole("button", { name: /Submit/i })).toBeDisabled(); + await page.waitForTimeout(clickTimeout); + + await expect(page.locator("[id='alert-network-number']")).toBeHidden(); + await expect(page.locator("[id='assigned-network-number']")).toHaveText(""); +});