Skip to content

Commit

Permalink
Re-write NN Assignment Form (#95)
Browse files Browse the repository at this point in the history
* get all that weak shit outta here boi

* rewrite nn assign form

* write test

* o you gotta throw it

* no password

* custom msg

* pretty

* chom

* oops needed OnSubmit
  • Loading branch information
WillNilges authored Nov 1, 2024
1 parent 467e0d9 commit f901942
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 62 deletions.
7 changes: 6 additions & 1 deletion app/nn-assign/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<main>
Expand Down
6 changes: 6 additions & 0 deletions components/NNAssignForm/NNAssignForm.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,9 @@
display: flex;
justify-content: center;
}

.alert {
display: flex;
justify-content: center;
align-items: center;
}
125 changes: 64 additions & 61 deletions components/NNAssignForm/NNAssignForm.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string | Blob | boolean | Number> = {};
event.forEach((value, key) => {
if (key === "install_number") {
data[key] = Number(value);
} else {
data[key] = value;
}
});
const {
register,
handleSubmit,
formState: { isDirty, isValid },
} = useForm<NNAssignRequestValues>();

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<NNAssignRequestValues> = (data) => {
console.log(data);
submitNNAssignRequest(data);
};

return (
<>
<div className={styles.formBody}>
<form action={sendForm}>
<form onSubmit={handleSubmit(onSubmit)}>
<h2>Request Network Number</h2>
<p>
Enter an install number and the Pre-Shared Key, and receive a
Expand All @@ -74,39 +69,47 @@ export function NNAssignForm() {
<br />
<div className={styles.horizontal}>
<input
{...register("install_number", {
required: "Please enter a valid Install Number",
})}
type="number"
name="install_number"
placeholder="Install Number"
required
/>
<input
{...register("password", {
required: "Please enter your pre-shared key",
})}
type="password"
name="password"
placeholder="Pre-Shared Key"
required
/>
</div>
<div className={styles.centered}>
<Button
type="submit"
disabled={disableSubmitButton}
hidden={disableSubmitButton}
disabled={isSubmitted || !isDirty || !isValid}
variant="contained"
size="large"
sx={{ width: "12rem", fontSize: "1rem", m: "1rem" }}
>
Submit
</Button>
</div>
<h3 hidden={!disableSubmitButton} className={styles.nnLabel}>
Your Network Number:
</h3>
<h1 hidden={!disableSubmitButton} id="">
{networkNumber}
</h1>
</form>
</div>
<ToastContainer />

<div data-testid="toasty" className="toasty">
<ToastContainer hideProgressBar={true} theme={"colored"} />
</div>
<div hidden={isNaN(parseInt(networkNumber))} id="alert-network-number">
<Alert>
<h3 className={styles.nnLabel} id="nn-message">
{nnMessage}
</h3>
<h1 id="assigned-network-number">{networkNumber}</h1>
</Alert>
</div>
</>
);
}
56 changes: 56 additions & 0 deletions tests/mock/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down Expand Up @@ -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 },
);
}),
];
90 changes: 90 additions & 0 deletions tests/nn_assign_form.spec.ts
Original file line number Diff line number Diff line change
@@ -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("");
});

0 comments on commit f901942

Please sign in to comment.