diff --git a/src/common-components/question-chain/question-chain.spec.js b/src/common-components/question-chain/question-chain.spec.js new file mode 100644 index 00000000..64260ddf --- /dev/null +++ b/src/common-components/question-chain/question-chain.spec.js @@ -0,0 +1,314 @@ +import { shallowMount } from "@vue/test-utils"; +import questionChain from "@/common-components/question-chain/question-chain.vue"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { nextTick } from "vue"; + +describe("Question Chain component", () => { + describe("on create...", () => { + test("Should populate questions data array", async () => { + //Arrange + const { wrapper } = setupMocks({}); + const expectedFirstQuestionAnswer = { + answerResult: "DB09410", + buttonLabel: "Yes", + nextQuestionSequence: null, + questionSequence: 1, + questionType: "answer", + value: "1|answer|DB09410|Yes", + }; + + //Act + await nextTick(); + + //Assert + expect(wrapper.vm.questions[0].answers[0]).toMatchObject(expectedFirstQuestionAnswer); + }); + + test("Should not include suppressed questions on questions data array", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + await wrapper.setData({ + questionData: [ + { + questionSequence: 1, + questionText: "Question here?", + answers: [ + { + answerResult: "DB09410", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "DB10840", + answerText: "No", + nextQuestionSequence: null, + }, + ], + suppressThisQuestion: true, + }, + ], + }); + + //Act + await nextTick(); + + //Assert + expect(wrapper.vm.questions[0]).toBeFalsy(); + }); + }); + + describe("method handleAnswer...", () => { + test("should run getQuestionChainAnswerIfComplete", () => { + //Arrange + const { wrapper } = setupMocks({}); + const testQuestion = {}; + const testReturnedAnswer = "aReturnedAnswer"; + wrapper.vm.getQuestionChainAnswerIfComplete = jest.fn(); + + //Act + wrapper.vm.handleAnswer(testQuestion, testReturnedAnswer); + + //Assert + expect(wrapper.vm.getQuestionChainAnswerIfComplete).toBeCalled(); + }); + + test("should emit update:modelValue if returnedAnswer contains 'answer'", () => { + //Arrange + const { wrapper } = setupMocks({}); + const testQuestion = {}; + const testReturnedAnswer = "1|answer|DB10840|No"; + + //Act + wrapper.vm.handleAnswer(testQuestion, testReturnedAnswer); + + //Assert + expect(wrapper.emitted()).toHaveProperty("update:modelValue"); + expect(wrapper.emitted()["update:modelValue"][0][0]).toMatchObject({ + answerResult: "DB10840", + }); + }); + + test("should NOT emit update:modelValue if no returnedAnswer", () => { + //Arrange + const { wrapper } = setupMocks({}); + const testQuestion = {}; + + //Act + wrapper.vm.handleAnswer(testQuestion); + + //Assert + expect(wrapper.emitted()).not.toHaveProperty("update:modelValue"); + }); + + test("should NOT emit update:modelValue if returnedAnswer contains 'nextQuestion'", () => { + //Arrange + const { wrapper } = setupMocks({}); + const testQuestion = {}; + const testReturnedAnswer = "1|nextQuestion|DB10840|No"; + + //Act + wrapper.vm.handleAnswer(testQuestion, testReturnedAnswer); + + //Assert + expect(wrapper.emitted()).not.toHaveProperty("update:modelValue"); + }); + }); + + describe("method getQuestionChainAnswerIfComplete...", () => { + test("should return false if no returned answer", () => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + const result = wrapper.vm.getQuestionChainAnswerIfComplete(); + + //Assert + expect(result).toBeFalsy(); + }); + + test("should set the answerSelected to match the answered one", async () => { + //Arrange + const { wrapper } = setupMocks({}); + const testReturnedAnswer = "1|answer|DB10840|No"; + await wrapper.setData({ + questionData: [ + { + questionSequence: 1, + questionText: "Question here?", + answers: [ + { + answerResult: "DB09410", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "DB10840", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + ], + }); + + //Act + wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer); + + //Assert + expect(wrapper.vm.questions[0].answerSelected).toBe("1|answer|DB10840|No"); + }); + + test("should remove answers of questions after the answered one", async () => { + //Arrange + const { wrapper } = setupMocks({}); + const testReturnedAnswer = "1|answer|DB10840|No"; + await wrapper.setData({ + questionData: [ + { + questionSequence: 1, + questionText: "Question here?", + answers: [ + { + answerResult: "DB09410", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "DB10840", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + { + questionSequence: 2, + questionText: "Question 2", + answers: [ + { + answerResult: "2-yes", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "2-no", + answerText: "No", + nextQuestionSequence: null, + }, + ], + answerSelected: "previously selected answer", + }, + ], + }); + + //Act + wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer); + + //Assert + expect(wrapper.vm.questions[1].answerSelected).toBeUndefined; + }); + + test("should return false if returnedAnswer is a nextQuestion (not a final answer)", async () => { + //Arrange + const { wrapper } = setupMocks({}); + const testReturnedAnswer = "1|nextQuestion|DB10840|No"; + + //Act + const result = wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer); + + //Assert + expect(result).toBeFalsy(); + }); + + test("should trigger scroll to .current-question if returnedAnswer is a nextQuestion (not a final answer)", async () => { + //Arrange + const { wrapper } = setupMocks({}); + const testReturnedAnswer = "1|nextQuestion|DB10840|No"; + + //Act + wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer); + + await nextTick(); + + //Assert + expect(Element.prototype.scrollIntoView).toHaveBeenCalled(); + }); + + test("should return answer object if returnedAnswer is a final matching answer", async () => { + //Arrange + const { wrapper } = setupMocks({}); + const testReturnedAnswer = "1|answer|DB10840|No"; + await wrapper.setData({ + questions: [ + { + questionSequence: 1, + questionText: "Question here?", + answers: [ + { + answerResult: "DB09410", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "DB10840", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + ], + index: 0, + }); + + //Act + const result = wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer); + + //Assert + expect(result).toMatchObject({ + answerResult: "DB10840", + answeredQuestions: [ + { questionNum: 1, questionText: "Question here?", selectedAnswerText: "No" }, + { + questionNum: 1, + questionText: "Does only your center sliding piece need to be replaced?", + selectedAnswerText: "No", + }, + ], + index: 0, + }); + }); + }); +}); + +function setupMocks({ + questionDataProp = [ + { + questionSequence: 1, + questionText: "Does only your center sliding piece need to be replaced?", + answers: [ + { + answerResult: "DB09410", + answerText: "Yes", + nextQuestionSequence: null, + }, + { + answerResult: "DB10840", + answerText: "No", + nextQuestionSequence: null, + }, + ], + }, + ], +}) { + Element.prototype.scrollIntoView = jest.fn(); + + const mountOptions = getMountOptions({}); + mountOptions.propsData = { + questionData: questionDataProp, + }; + mountOptions["attachTo"] = document.body; + + const wrapper = shallowMount(questionChain, mountOptions); + + return { wrapper }; +} diff --git a/src/common-components/question-chain/question-chain.vue b/src/common-components/question-chain/question-chain.vue new file mode 100644 index 00000000..11874670 --- /dev/null +++ b/src/common-components/question-chain/question-chain.vue @@ -0,0 +1,163 @@ + + +