diff --git a/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.test.tsx b/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.test.tsx
new file mode 100644
index 000000000..ded10545e
--- /dev/null
+++ b/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.test.tsx
@@ -0,0 +1,72 @@
+// @vitest-environment happy-dom
+import { render, screen, waitFor } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, test, vi } from "vitest";
+
+import { TooltipProvider } from "@/components";
+import { configurationAtom, getAppStore } from "@/core";
+import { createQueryClient } from "@/core/queryClient";
+import { TestProvider } from "@/utils/testing";
+
+import CreateConnection from "./CreateConnection";
+
+function renderCreateConnection() {
+ const store = getAppStore();
+ store.set(configurationAtom, new Map());
+
+ render(
+
+
+
+
+ ,
+ );
+
+ return store;
+}
+
+describe("CreateConnection", () => {
+ test("removes newlines and surrounding whitespace from URL fields", async () => {
+ const user = userEvent.setup();
+ const store = renderCreateConnection();
+
+ await user.type(
+ screen.getByRole("textbox", { name: "Public or Proxy Endpoint" }),
+ " https://proxy.example.com/{Enter}path ",
+ );
+ await user.click(
+ screen.getByRole("checkbox", { name: "Using Proxy-Server" }),
+ );
+ await user.type(
+ screen.getByRole("textbox", { name: "Graph Connection URL" }),
+ " https://database.example.com/{Enter}graph ",
+ );
+ await user.click(screen.getByRole("button", { name: "Add Connection" }));
+
+ await waitFor(() => {
+ expect(store.get(configurationAtom)).toHaveLength(1);
+ });
+
+ const [savedConnection] = store.get(configurationAtom).values();
+ expect(savedConnection).toMatchObject({
+ connection: {
+ url: "https://proxy.example.com/path",
+ graphDbUrl: "https://database.example.com/graph",
+ },
+ });
+ });
+
+ test("rejects a URL that is empty after normalization", async () => {
+ const user = userEvent.setup();
+ const store = renderCreateConnection();
+
+ await user.type(
+ screen.getByRole("textbox", { name: "Public or Proxy Endpoint" }),
+ " {Enter} ",
+ );
+ await user.click(screen.getByRole("button", { name: "Add Connection" }));
+
+ expect(store.get(configurationAtom)).toHaveLength(0);
+ expect(screen.getByText("URL is required")).toBeInTheDocument();
+ });
+});
diff --git a/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.tsx b/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.tsx
index ac6bb5a1f..1a2ee1cbc 100644
--- a/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.tsx
+++ b/packages/graph-explorer/src/modules/CreateConnection/CreateConnection.tsx
@@ -50,6 +50,10 @@ type ConnectionForm = {
nodeExpansionLimit?: number;
};
+function normalizeUrlField(value: string | undefined) {
+ return value?.replace(/[\r\n]/g, "").trim();
+}
+
const CONNECTIONS_OP: {
label: string;
value: QueryEngine;
@@ -234,22 +238,35 @@ const CreateConnection = ({
const reset = useResetState();
const onSubmit = () => {
- if (!form.name || !form.url || !form.queryEngine) {
+ const normalizedForm: ConnectionForm = {
+ ...form,
+ url: normalizeUrlField(form.url),
+ graphDbUrl: normalizeUrlField(form.graphDbUrl),
+ };
+
+ if (
+ !normalizedForm.name ||
+ !normalizedForm.url ||
+ !normalizedForm.queryEngine
+ ) {
setError(true);
return;
}
- if (form.proxyConnection && !form.graphDbUrl) {
+ if (normalizedForm.proxyConnection && !normalizedForm.graphDbUrl) {
setError(true);
return;
}
- if (form.awsAuthEnabled && (!form.awsRegion || !form.serviceType)) {
+ if (
+ normalizedForm.awsAuthEnabled &&
+ (!normalizedForm.awsRegion || !normalizedForm.serviceType)
+ ) {
setError(true);
return;
}
- onSave(form as Required);
+ onSave(normalizedForm as Required);
reset();
onClose();
};
@@ -292,7 +309,9 @@ const CreateConnection = ({
onChange={onFormChange("url")}
errorMessage="URL is required"
placeholder="https://example.com"
- validationState={hasError && !form.url ? "invalid" : "valid"}
+ validationState={
+ hasError && !normalizeUrlField(form.url) ? "invalid" : "valid"
+ }
/>
@@ -317,7 +336,9 @@ const CreateConnection = ({
errorMessage="URL is required"
placeholder="https://neptune-cluster.amazonaws.com"
validationState={
- hasError && !form.graphDbUrl ? "invalid" : "valid"
+ hasError && !normalizeUrlField(form.graphDbUrl)
+ ? "invalid"
+ : "valid"
}
/>