import { getLocalVue } from "@tests/vitest/helpers"; import { mount } from "@vue/test-utils"; import flushPromises from "flush-promises"; import { describe, expect, it } from "vitest"; import { useServerMock } from "@/api/client/__mocks__"; import { OK_PLUGIN_STATUS } from "@/components/ConfigTemplates/test_fixtures"; import type { ObjectStoreTemplateSummary } from "@/components/ObjectStore/Templates/types"; import type { UserConcreteObjectStore } from "./types"; import CreateForm from "./CreateForm.vue"; const FAKE_OBJECT_STORE: UserConcreteObjectStore = { name: "My New Name", template_id: "moo", template_version: 0, active: true, badges: [], hidden: false, private: false, purged: false, quota: { enabled: false, }, type: "aws_s3", uuid: "test_UUID", variables: { myvar: "default", }, secrets: ["mysecret"], }; const STANDARD_TEMPLATE: ObjectStoreTemplateSummary = { type: "aws_s3", name: "moo", description: null, variables: [ { name: "myvar", type: "string", help: "*myvar help*", default: "default", }, ], secrets: [ { name: "mysecret", help: "**mysecret help**", }, ], id: "moo", version: 0, badges: [], hidden: false, }; const SIMPLE_TEMPLATE: ObjectStoreTemplateSummary = { type: "aws_s3", name: "moo", description: null, variables: [ { name: "myvar", type: "string", help: "*myvar help*", default: "default", }, ], secrets: [ { name: "mysecret", help: "**mysecret help**", }, ], id: "moo", version: 0, badges: [], hidden: false, }; const OPTIONAL_SECRET_TEMPLATE: ObjectStoreTemplateSummary = { type: "aws_s3", name: "moo", description: null, variables: [ { name: "myvar", type: "string", help: "myvar help", default: "default", }, ], secrets: [ { name: "optional_secret", help: "An optional secret", optional: true, }, ], id: "moo", version: 0, badges: [], hidden: false, }; const OPTIONAL_VAR_WITH_VALIDATION_TEMPLATE: ObjectStoreTemplateSummary = { type: "aws_s3", name: "moo", description: null, variables: [ { name: "optional_var", type: "string", help: "optional var help", default: "", optional: true, validators: [ { type: "length", min: 5, message: "Must be at least 5 characters", negate: false, implicit: false, }, ], }, ], secrets: [ { name: "mysecret", help: "mysecret help", optional: true, }, ], id: "moo", version: 0, badges: [], hidden: false, }; const localVue = getLocalVue(true); const { server, http } = useServerMock(); describe("CreateForm", () => { it("should render a form with admin markdown converted to HTML in help", async () => { const wrapper = mount(CreateForm as object, { propsData: { template: STANDARD_TEMPLATE, }, localVue, }); await flushPromises(); const varFormEl = wrapper.find("#form-element-myvar"); expect(varFormEl).toBeTruthy(); expect(varFormEl.html()).toContain("myvar help"); const secretFormEl = wrapper.find("#form-element-mysecret"); expect(secretFormEl).toBeTruthy(); expect(secretFormEl.html()).toContain("mysecret help"); }); it("should post to create a new object store on submit", async () => { const wrapper = mount(CreateForm as object, { propsData: { template: SIMPLE_TEMPLATE, }, localVue, }); server.use( http.post("/api/object_store_instances", ({ response }) => { return response(200).json(FAKE_OBJECT_STORE); }), http.post("/api/object_store_instances/test", ({ response }) => { return response(200).json(OK_PLUGIN_STATUS); }), ); const nameForElement = wrapper.find("#form-element-_meta_name"); await nameForElement.find("input").setValue("My New Name"); const passwordElement = wrapper.find("#form-element-mysecret"); await passwordElement.find("input").setValue("mysecretvalue"); const submitElement = wrapper.find("#submit"); await submitElement.trigger("click"); await flushPromises(); const emitted = wrapper.emitted("created") || []; expect(emitted).toHaveLength(1); expect(emitted[0][0]).toMatchObject(FAKE_OBJECT_STORE); }); it("should indicate an error on failure", async () => { const wrapper = mount(CreateForm as object, { propsData: { template: SIMPLE_TEMPLATE, }, localVue, }); server.use( http.post("/api/object_store_instances", ({ response }) => { return response("4XX").json({ err_msg: "Error creating this", err_code: 400 }, { status: 400 }); }), http.post("/api/object_store_instances/test", ({ response }) => { return response(200).json(OK_PLUGIN_STATUS); }), ); await flushPromises(); const nameForElement = wrapper.find("#form-element-_meta_name"); nameForElement.find("input").setValue("My New Name"); const passwordElement = wrapper.find("#form-element-mysecret"); await passwordElement.find("input").setValue("mysecretvalue"); expect(wrapper.find("[data-description='object-store-creation-error']").exists()).toBe(false); const submitElement = wrapper.find("#submit"); await submitElement.trigger("click"); await flushPromises(); const emitted = wrapper.emitted("created") || []; expect(emitted).toHaveLength(0); const errorEl = wrapper.find("[data-description='object-store-creation-error']"); expect(errorEl.exists()).toBe(true); expect(errorEl.html()).toContain("Error creating this"); }); it("should disable submit when there are validation errors", async () => { const wrapper = mount(CreateForm as object, { propsData: { template: SIMPLE_TEMPLATE, }, localVue, }); const nameForElement = wrapper.find("#form-element-_meta_name"); await nameForElement.find("input").setValue("My New Name"); // Leave secret empty to trigger validation error const submitElement = wrapper.find("#submit"); expect(submitElement.classes().includes("g-disabled")).toBe(true); // Try to click when submit is disabled will not emit created event await submitElement.trigger("click"); await flushPromises(); const emitted = wrapper.emitted("created") || []; expect(emitted).toHaveLength(0); }); it("should allow submission when optional secret is empty", async () => { server.use( http.post("/api/object_store_instances", ({ response }) => { return response(200).json(FAKE_OBJECT_STORE); }), http.post("/api/object_store_instances/test", ({ response }) => { return response(200).json(OK_PLUGIN_STATUS); }), ); const wrapper = mount(CreateForm as object, { propsData: { template: OPTIONAL_SECRET_TEMPLATE, }, localVue, }); const nameForElement = wrapper.find("#form-element-_meta_name"); await nameForElement.find("input").setValue("My New Name"); // Don't fill in the optional secret const submitElement = wrapper.find("#submit"); expect(submitElement.classes().includes("g-disabled")).toBe(false); await submitElement.trigger("click"); await flushPromises(); const emitted = wrapper.emitted("created") || []; expect(emitted).toHaveLength(1); }); it("should validate optional fields when they have values", async () => { const wrapper = mount(CreateForm as object, { propsData: { template: OPTIONAL_VAR_WITH_VALIDATION_TEMPLATE, }, localVue, }); const nameForElement = wrapper.find("#form-element-_meta_name"); await nameForElement.find("input").setValue("My New Name"); // Set optional field to a value that's too short const optionalVarElement = wrapper.find("#form-element-optional_var"); await optionalVarElement.find("input").setValue("abc"); // Too short (< 5 chars) await flushPromises(); const submitElement = wrapper.find("#submit"); expect(submitElement.classes().includes("g-disabled")).toBe(true); // Fix the validation error await optionalVarElement.find("input").setValue("validvalue"); await flushPromises(); expect(submitElement.classes().includes("g-disabled")).toBe(false); }); });