import { shallowMount } from "@vue/test-utils"; import { defineRule } from "vee-validate"; import textboxQuestion from "./textbox-question"; // Mock CMS content const questionText = "Question Text"; const mockMixin = { methods: { getCmsContent: jest.fn().mockImplementation(() => { return questionText; }), }, }; const maska = jest.fn(); // Real rules registered with vee-validate so the watcher's pre-check (which calls the // standalone `validate` export, not `this.validate`) exercises genuine valid/invalid paths. defineRule("spec-always-valid", () => true); defineRule("spec-always-invalid", () => "invalid"); describe("textboxQuestion.vue", () => { it("Should coerce non-string modelValue to empty string for v-model.trim", async () => { const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: null, }, mixins: [mockMixin], }); expect(wrapper.vm.value).toBe(""); }); it("Should render a text input", async () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, mixins: [mockMixin], }); wrapper.getCmsContent = jest.fn(); // Act const input = wrapper.find("input"); // Assert expect(input.exists()).toBe(true); }); it("Should return rounded-pill class", async () => { // Act const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, mixins: [mockMixin], propsData: { cornerStyle: "rounded", }, }); // Assert const paragraph = wrapper.find("input"); expect(paragraph.attributes("class")).toContain("rounded-pill"); }); it("Should return form-control class", async () => { // Act const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, mixins: [mockMixin], propsData: { questionAlignment: "text", }, }); // Assert const paragraph = wrapper.find("input"); expect(paragraph.attributes("class")).toContain("form-control"); }); it("Should render the 'questionText' data value as the aria-label attribute.", async () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, mixins: [mockMixin], }); // Act const label = wrapper.find("label"); // Assert expect(label.attributes("aria-label")).toContain(questionText); }); it("Should return aria-disabled state as disabled", async () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { isDisabled: true, }, mixins: [mockMixin], }); // Assert const input = wrapper.find("input"); // Expect expect(input.attributes("aria-disabled")).toEqual("true"); }); it("Should emit new value when modelValue is changed", async () => { // Act const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "val", }, mixins: [mockMixin], }); await wrapper.find("input").setValue("val2"); // Assert expect(wrapper.emitted()).toHaveProperty("change"); }); it("Should call this.handleChange with new value and shouldValidate=true when the value is changed and the new value is valid", async () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "foo", validationRules: "spec-always-valid", }, mixins: [mockMixin], }); wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); // Act await wrapper.vm.$options.watch.value.call(wrapper.vm, "bar"); // Assert expect(wrapper.vm.handleChange).toHaveBeenCalledWith("bar", true); }); it("Should still call this.handleChange with shouldValidate=false when the new value is invalid, so the field stays in sync for later validation", async () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "foo", validationRules: "spec-always-invalid", }, mixins: [mockMixin], }); wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); // Act await wrapper.vm.$options.watch.value.call(wrapper.vm, "bar"); // Assert expect(wrapper.vm.handleChange).toHaveBeenCalledWith("bar", false); }); it("Should not display camera or spinner icon when disabled", () => { // Arrange const inputId = "test"; const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "", includeImageQuestion: true, inputId: inputId, isDisabled: true, }, mixins: [mockMixin], attachTo: document.body, }); // Act const cameraIcon = wrapper.find('[data-test="image-upload"]'); const loadingIcon = wrapper.findComponent("loader"); // Assert expect(cameraIcon.exists()).toBe(false); expect(loadingIcon.exists()).toBe(false); }); describe("Image Uploading", () => { it("Should call this.handleChange & emit update when fully valid image is submitted.", async () => { // Arrange const inputId = "test"; const responseValue = "testResponse"; const imageHandler = jest.fn().mockImplementation( () => new Promise((resolve, reject) => { resolve({ data: [responseValue], }); }) ); const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "", "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }), includeImageQuestion: true, inputId: inputId, imageQuestionSubmitHandler: imageHandler, }, mixins: [mockMixin], attachTo: document.body, }); wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); wrapper.vm.isImageValid = jest.fn().mockImplementation(() => true); // Act const imageUploadField = wrapper.find('[data-test="image-upload"]'); await imageUploadField.trigger("change"); await wrapper.vm.$nextTick(); // Assert expect(wrapper.vm.imageQuestionSubmitHandler).toHaveBeenCalled(); expect(wrapper.vm.handleChange).toHaveBeenCalled(); expect(wrapper.emitted()).toHaveProperty("update:modelValue"); }); it("Should not call handler and emit image-lookup-error when image is invalid.", async () => { // Arrange const inputId = "test"; const responseValue = "testResponse"; const imageHandler = jest.fn().mockImplementation( () => new Promise((resolve, reject) => { resolve({ data: [responseValue], }); }) ); const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "", "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }), includeImageQuestion: true, inputId: inputId, imageQuestionSubmitHandler: imageHandler, }, mixins: [mockMixin], attachTo: document.body, }); wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); wrapper.vm.isImageValid = jest.fn().mockImplementation(() => false); // Act const imageUploadField = wrapper.find('[data-test="image-upload"]'); await imageUploadField.trigger("change"); await wrapper.vm.$nextTick(); // Assert expect(wrapper.vm.imageQuestionSubmitHandler).not.toHaveBeenCalled(); expect(wrapper.vm.handleChange).not.toHaveBeenCalled(); expect(wrapper.emitted()).toHaveProperty("imageValidityError"); }); it("Should emit image-lookup-error when image lookup responds with an error.", async () => { // Arrange const inputId = "test"; const imageHandler = jest.fn().mockImplementation( () => new Promise((resolve, reject) => { reject({}); }) ); const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { modelValue: "", "onUpdate:modelValue": (v) => wrapper.setProps({ modelValue: v }), includeImageQuestion: true, inputId: inputId, imageQuestionSubmitHandler: imageHandler, }, mixins: [mockMixin], attachTo: document.body, }); wrapper.vm.handleChange = jest.fn().mockImplementation(() => {}); wrapper.vm.isImageValid = jest.fn().mockImplementation(() => true); // Act const imageUploadField = wrapper.find('[data-test="image-upload"]'); await imageUploadField.trigger("change"); await wrapper.vm.$nextTick(); // Assert expect(wrapper.vm.imageQuestionSubmitHandler).toHaveBeenCalled(); expect(wrapper.vm.handleChange).not.toHaveBeenCalled(); expect(wrapper.emitted()).toHaveProperty("imageLookupError"); }); }); describe("Image Validation", () => { it("Should return true if a valid image is tested.", () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { inputId: "input ID", maxFileSize: 5140 * 1028, }, mixins: [mockMixin], }); const image = { size: 100 * 1028, }; // Act const result = wrapper.vm.isImageValid(image); // Assert expect(result).toBeTruthy(); }); it("Should return false if null image is tested.", () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { inputId: "input ID", maxFileSize: 5140 * 1028, }, mixins: [mockMixin], }); const image = null; // Act const result = wrapper.vm.isImageValid(image); // Assert expect(result).toBeFalsy(); }); it("Should return false if oversized image is tested.", () => { // Arrange const wrapper = shallowMount(textboxQuestion, { global: { directives: { maska: maska, }, }, propsData: { inputId: "input ID", maxFileSize: 5140 * 1028, }, mixins: [mockMixin], }); const image = { size: 10000000 * 1028, }; // Act const result = wrapper.vm.isImageValid(image); // Assert expect(result).toBeFalsy(); }); }); });