From 2a039209336a6b6a793eac8489f53beab8fea5bc Mon Sep 17 00:00:00 2001 From: David Mears Date: Thu, 12 Sep 2024 11:54:46 +0100 Subject: [PATCH] Use JSON data for request bodies instead of FormData --- components/ParameterForm.vue | 11 ++----- server/api/scenarios.post.ts | 6 ++-- tests/integration/rApi.spec.ts | 36 ++++++++------------- tests/unit/components/ParameterForm.spec.ts | 13 +++++--- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/components/ParameterForm.vue b/components/ParameterForm.vue index 18fec7e7..c9024e65 100644 --- a/components/ParameterForm.vue +++ b/components/ParameterForm.vue @@ -68,11 +68,11 @@ color="primary" :size="appStore.largeScreen ? 'lg' : undefined" type="submit" - :disabled="formSubmitting" + :disabled="formSubmitting || props.metadataFetchStatus === 'error'" @click="submitForm" > Run - + @@ -134,15 +134,10 @@ const submitForm = async () => { return; }; - const formDataObject = new FormData(); - Object.entries(formData.value).forEach(([key, value]) => { - formDataObject.append(key, value.toString()); - }); - formSubmitting.value = true; const response = await $fetch("/api/scenarios", { method: "POST", - body: formDataObject, + body: { parameters: formData.value }, }).catch((error: FetchError) => { console.error(error); }); diff --git a/server/api/scenarios.post.ts b/server/api/scenarios.post.ts index a491e804..9a443917 100644 --- a/server/api/scenarios.post.ts +++ b/server/api/scenarios.post.ts @@ -1,12 +1,12 @@ import { runScenario } from "@/server/handlers/scenarios"; import { defineRApiEventHandler } from "@/server/utils/defineRApiEventHandler"; -import { formDataToObject } from "@/server/utils/helpers"; import type { NewScenarioResponse } from "@/types/apiResponseTypes"; export default defineRApiEventHandler( async (event): Promise => { - const formDataBody = await readFormData(event); - const newScenarioResponse = await runScenario(formDataToObject(formDataBody), event); + const body = await readBody(event); + const parameters = typeof body === "object" ? body.parameters : JSON.parse(body).parameters; // Comes through as a string in the integration tests. + const newScenarioResponse = await runScenario(parameters, event); return newScenarioResponse; }, ); diff --git a/tests/integration/rApi.spec.ts b/tests/integration/rApi.spec.ts index de0cb48e..b7bf1564 100644 --- a/tests/integration/rApi.spec.ts +++ b/tests/integration/rApi.spec.ts @@ -127,13 +127,11 @@ describe("endpoints which consume the R API", { sequential: true }, async () => // instead use the mockoonResponse parameter to tell Mockoon which type of response to send. describe("post api/scenarios", async () => { it("returns a successful response when the mock server responds successfully", async () => { - const formData = new FormData(); - formData.append("mockoonResponse", "successful"); - formData.append("country", "Thailand"); - formData.append("pathogen", "sars-cov-1"); - formData.append("response", "no_closure"); - formData.append("vaccine", "none"); - const response = await nuxtTestUtilsFetch(`/api/scenarios`, { method: "POST", body: formData }); + const body = JSON.stringify( + { parameters: { mockoonResponse: "successful", country: "Thailand", pathogen: "sars-cov-1", response: "no_closure", vaccine: "none" } }, + ); + + const response = await nuxtTestUtilsFetch(`/api/scenarios`, { method: "POST", body }); expect(response.ok).toBe(true); expect(response.status).toBe(200); @@ -144,14 +142,11 @@ describe("endpoints which consume the R API", { sequential: true }, async () => }); it("returns a response with informative errors when the mock server responds with an error", async () => { - const formData = new FormData(); - formData.append("mockoonResponse", "notFound"); - formData.append("country", "Thailand"); - formData.append("pathogen", "sars-cov-1"); - formData.append("response", "no_closure"); - formData.append("vaccine", "none"); + const body = JSON.stringify( + { parameters: { mockoonResponse: "notFound", country: "Thailand", pathogen: "sars-cov-1", response: "no_closure", vaccine: "none" } }, + ); - const response = await nuxtTestUtilsFetch(`/api/scenarios`, { method: "POST", body: formData }); + const response = await nuxtTestUtilsFetch(`/api/scenarios`, { method: "POST", body }); expect(response.ok).toBe(false); expect(response.status).toBe(404); @@ -163,14 +158,11 @@ describe("endpoints which consume the R API", { sequential: true }, async () => }); it("returns a response with informative errors when the mock server doesn't respond in time", async () => { - const formData = new FormData(); - formData.append("mockoonResponse", "delayed"); - formData.append("country", "Thailand"); - formData.append("pathogen", "sars-cov-1"); - formData.append("response", "no_closure"); - formData.append("vaccine", "none"); - - const response = await nuxtTestUtilsFetch(`/api/scenarios`, { method: "POST", body: formData }); + const body = JSON.stringify( + { parameters: { mockoonResponse: "delayed", country: "Thailand", pathogen: "sars-cov-1", response: "no_closure", vaccine: "none" } }, + ); + + const response = await nuxtTestUtilsFetch(`/api/scenarios`, { method: "POST", body }); expect(response.ok).toBe(false); expect(response.status).toBe(500); diff --git a/tests/unit/components/ParameterForm.spec.ts b/tests/unit/components/ParameterForm.spec.ts index c23cd264..fd302544 100644 --- a/tests/unit/components/ParameterForm.spec.ts +++ b/tests/unit/components/ParameterForm.spec.ts @@ -1,9 +1,10 @@ import { describe, expect, it } from "vitest"; import { mountSuspended, registerEndpoint } from "@nuxt/test-utils/runtime"; import { FetchError } from "ofetch"; +import { readBody } from "h3"; import { flushPromises } from "@vue/test-utils"; +import { waitFor } from "@testing-library/vue"; -import { formDataToObject } from "@/server/utils/helpers"; import type { Metadata } from "@/types/apiResponseTypes"; import ParameterForm from "@/components/ParameterForm.vue"; @@ -110,7 +111,7 @@ describe("parameter form", () => { registerEndpoint("/api/scenarios", { method: "POST", async handler(event) { - const parameters = formDataToObject(event.node.req.body) as Record; + const { parameters } = await readBody(event); if (parameters.long_list === "1" && parameters.region === "HVN" && parameters.short_list === "no") { return { runId: "randomId" }; @@ -134,9 +135,11 @@ describe("parameter form", () => { // I couldn't find a way to spy on the navigateTo function, so this is a second-best test that we // at least construct the correct path to navigate to. const cForm = component.findComponent({ name: "CForm" }); - expect( - cForm.element.attributes.getNamedItem("data-test-navigate-to")!.value, - ).toBe("/scenarios/randomId"); + await waitFor(() => { + expect( + cForm.element.attributes.getNamedItem("data-test-navigate-to")!.value, + ).toBe("/scenarios/randomId"); + }); }); it("displays CAlert with error message when metadataFetchStatus is 'error'", async () => {