654 lines
25 KiB
JavaScript
654 lines
25 KiB
JavaScript
import { shallowMount } from "@vue/test-utils";
|
|
import questionChain from "./question-chain.vue";
|
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
|
import { nextTick } from "vue";
|
|
|
|
jest.mock(
|
|
"@/store",
|
|
() => {
|
|
return {};
|
|
},
|
|
{ virtual: true }
|
|
);
|
|
|
|
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 for nextQuestion during normal forward progress", () => {
|
|
//Arrange
|
|
const { wrapper } = setupMocks({});
|
|
const testQuestion = {};
|
|
const testReturnedAnswer = "1|nextQuestion|3|No";
|
|
|
|
//Act
|
|
wrapper.vm.handleAnswer(testQuestion, testReturnedAnswer);
|
|
|
|
//Assert
|
|
expect(wrapper.emitted()).not.toHaveProperty("update:modelValue");
|
|
});
|
|
|
|
test("should emit update:modelValue for nextQuestion when revising a saved answer", async () => {
|
|
//Arrange
|
|
const { wrapper } = setupMocks({});
|
|
await wrapper.setProps({ hasSavedAnswer: true });
|
|
const testQuestion = {};
|
|
const testReturnedAnswer = "1|nextQuestion|3|No";
|
|
|
|
//Act
|
|
wrapper.vm.handleAnswer(testQuestion, testReturnedAnswer);
|
|
|
|
//Assert
|
|
expect(wrapper.emitted()).toHaveProperty("update:modelValue");
|
|
expect(wrapper.emitted()["update:modelValue"][0][0]).toMatchObject({
|
|
incomplete: true,
|
|
index: 0,
|
|
});
|
|
});
|
|
});
|
|
|
|
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 follow the No path and show the next question when changing Q1 from Yes to No", async () => {
|
|
const { wrapper } = setupMocks({
|
|
questionDataProp: [
|
|
{
|
|
questionSequence: 1,
|
|
questionText: "Is this the Overland edition?",
|
|
answers: [
|
|
{
|
|
answerResult: null,
|
|
answerText: "Yes",
|
|
nextQuestionSequence: 2,
|
|
},
|
|
{
|
|
answerResult: null,
|
|
answerText: "No",
|
|
nextQuestionSequence: 5,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 2,
|
|
questionText: "Rain sensing wipers?",
|
|
answers: [
|
|
{ answerResult: "PART-A", answerText: "Yes", nextQuestionSequence: 3 },
|
|
{ answerResult: "PART-B", answerText: "No", nextQuestionSequence: 3 },
|
|
],
|
|
answerSelected: "2|answer|PART-A|Yes",
|
|
},
|
|
{
|
|
questionSequence: 3,
|
|
questionText: "Laminated door glass?",
|
|
answers: [
|
|
{
|
|
answerResult: "PART-C",
|
|
answerText: "Yes",
|
|
nextQuestionSequence: null,
|
|
},
|
|
{
|
|
answerResult: "PART-D",
|
|
answerText: "No",
|
|
nextQuestionSequence: null,
|
|
},
|
|
],
|
|
answerSelected: "3|answer|PART-C|Yes",
|
|
},
|
|
{
|
|
questionSequence: 5,
|
|
questionText: "Factory installed antenna?",
|
|
answers: [
|
|
{
|
|
answerResult: "PART-E",
|
|
answerText: "Yes",
|
|
nextQuestionSequence: null,
|
|
},
|
|
{
|
|
answerResult: "PART-F",
|
|
answerText: "No",
|
|
nextQuestionSequence: null,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
await wrapper.setProps({ index: 0 });
|
|
|
|
const result = wrapper.vm.getQuestionChainAnswerIfComplete("1|nextQuestion|5|No");
|
|
|
|
expect(result.incomplete).toBe(true);
|
|
expect(wrapper.vm.questions[1].answerSelected).toBeUndefined();
|
|
expect(wrapper.vm.questions[2].answerSelected).toBeUndefined();
|
|
expect(wrapper.vm.currentQuestionNum).toBe(5);
|
|
expect(result.answeredQuestions).toHaveLength(1);
|
|
expect(result.answeredQuestions[0].selectedAnswerText).toBe("No");
|
|
});
|
|
|
|
test("should return incomplete answer if returnedAnswer is a nextQuestion (not a final answer)", async () => {
|
|
//Arrange
|
|
const { wrapper } = setupMocks({});
|
|
const testReturnedAnswer = "1|nextQuestion|3|No";
|
|
|
|
//Act
|
|
const result = wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer);
|
|
|
|
//Assert
|
|
expect(result).toMatchObject({
|
|
incomplete: true,
|
|
index: 0,
|
|
});
|
|
});
|
|
|
|
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|3|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({
|
|
questionDataProp: [
|
|
{
|
|
questionSequence: 1,
|
|
questionText: "Question here?",
|
|
answers: [
|
|
{
|
|
answerResult: "DB09410",
|
|
answerText: "Yes",
|
|
nextQuestionSequence: null,
|
|
problemQuestionId: 9529,
|
|
},
|
|
{
|
|
answerResult: "DB10840",
|
|
answerText: "No",
|
|
nextQuestionSequence: null,
|
|
problemQuestionId: 9531,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
const testReturnedAnswer = "1|answer|DB10840|No";
|
|
await wrapper.setProps({ index: 0 });
|
|
await nextTick();
|
|
|
|
//Act
|
|
const result = wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer);
|
|
|
|
//Assert
|
|
expect(result).toMatchObject({
|
|
answerResult: "DB10840",
|
|
problemQuestionId: 9531,
|
|
index: 0,
|
|
});
|
|
expect(result.answeredQuestions).toEqual([
|
|
{
|
|
questionText: "Question here?",
|
|
selectedAnswer: "1|answer|DB10840|No",
|
|
selectedAnswerText: "No",
|
|
questionNum: 1,
|
|
problemQuestionId: 9531,
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("should include problemQuestionId from parts-or-questions answer on created", async () => {
|
|
//Arrange
|
|
const { wrapper } = setupMocks({
|
|
questionDataProp: [
|
|
{
|
|
questionSequence: 1,
|
|
questionText:
|
|
"Does the rubber seal around your windshield have a chrome strip running through it?",
|
|
answers: [
|
|
{
|
|
answerResult: "WCR 848",
|
|
answerText: "Yes",
|
|
nextQuestionSequence: null,
|
|
problemQuestionId: 9531,
|
|
},
|
|
{
|
|
answerResult: "WCR 848",
|
|
answerText: "No",
|
|
nextQuestionSequence: null,
|
|
problemQuestionId: 9529,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
//Act
|
|
await nextTick();
|
|
|
|
//Assert
|
|
expect(wrapper.vm.questions[0].answers[0].problemQuestionId).toBe(9531);
|
|
expect(wrapper.vm.questions[0].answers[1].problemQuestionId).toBe(9529);
|
|
});
|
|
|
|
test("should normalize uppercase answerSelected to match button value on created", async () => {
|
|
const { wrapper } = setupMocks({
|
|
questionDataProp: [
|
|
{
|
|
questionSequence: 1,
|
|
questionText: "Is your vehicle equipped with rain sensing wipers?",
|
|
answerSelected: "1|ANSWER|DW01705|YES",
|
|
answers: [
|
|
{
|
|
answerResult: "DW01705",
|
|
answerText: "Yes",
|
|
nextQuestionSequence: null,
|
|
},
|
|
{
|
|
answerResult: "DW01591",
|
|
answerText: "No",
|
|
nextQuestionSequence: null,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
await nextTick();
|
|
|
|
expect(wrapper.vm.questions[0].answerSelected).toBe("1|answer|DW01705|Yes");
|
|
});
|
|
test("should show question 3 after answering No on Cherokee Overland question", async () => {
|
|
const { wrapper } = setupMocks({
|
|
questionDataProp: getCherokeeWindshieldQuestions(),
|
|
});
|
|
await nextTick();
|
|
|
|
const q1 = wrapper.vm.questions[0];
|
|
wrapper.vm.handleAnswer(q1, "1|nextQuestion|3|No");
|
|
|
|
expect(wrapper.vm.currentQuestionNum).toBe(3);
|
|
expect(
|
|
wrapper.vm.questions.filter((question) => wrapper.vm.isQuestionVisible(question))
|
|
).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ questionSequence: 1 }),
|
|
expect.objectContaining({ questionSequence: 3 }),
|
|
])
|
|
);
|
|
expect(
|
|
wrapper.vm.questions.filter((question) => wrapper.vm.isQuestionVisible(question))
|
|
).not.toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ questionSequence: 2 })])
|
|
);
|
|
});
|
|
|
|
test("should resume at question 3 after remount when Q1 No was already saved", async () => {
|
|
const questionDataProp = getCherokeeWindshieldQuestions();
|
|
questionDataProp[0].answerSelected = "1|nextQuestion|3|No";
|
|
|
|
const { wrapper } = setupMocks({ questionDataProp });
|
|
await nextTick();
|
|
|
|
expect(wrapper.vm.currentQuestionNum).toBe(3);
|
|
expect(wrapper.vm.isQuestionVisible(wrapper.vm.questions[2])).toBe(true);
|
|
});
|
|
|
|
test("should resolve on Q1 Yes and not show Q2 for Honda HUD question", async () => {
|
|
const { wrapper } = setupMocks({
|
|
questionDataProp: getHondaAccordWindshieldQuestions(),
|
|
});
|
|
await nextTick();
|
|
|
|
const q1 = wrapper.vm.questions[0];
|
|
wrapper.vm.handleAnswer(q1, "1|answer|FW04796|Yes");
|
|
|
|
expect(wrapper.emitted()["update:modelValue"][0][0]).toMatchObject({
|
|
answerResult: "FW04796",
|
|
index: 0,
|
|
});
|
|
expect(wrapper.vm.currentQuestionNum).toBe(0);
|
|
expect(wrapper.vm.isQuestionVisible(wrapper.vm.questions[1])).toBe(false);
|
|
});
|
|
|
|
test("should resolve on Q3 No and not continue after remount", async () => {
|
|
const questionDataProp = getHondaAccordWindshieldQuestions();
|
|
questionDataProp[0].answerSelected = "1|nextQuestion|2|No";
|
|
questionDataProp[1].answerSelected = "2|nextQuestion|3|No";
|
|
questionDataProp[2].answerSelected = "3|answer|FW04793|No";
|
|
|
|
const { wrapper } = setupMocks({ questionDataProp });
|
|
await nextTick();
|
|
|
|
expect(wrapper.vm.currentQuestionNum).toBe(0);
|
|
expect(wrapper.vm.isQuestionVisible(wrapper.vm.questions[2])).toBe(true);
|
|
expect(
|
|
wrapper.vm.questions.filter((question) => wrapper.vm.isQuestionVisible(question))
|
|
).toHaveLength(3);
|
|
});
|
|
|
|
test("should show Q2 after answering No on Honda HUD question", async () => {
|
|
const { wrapper } = setupMocks({
|
|
questionDataProp: getHondaAccordWindshieldQuestions(),
|
|
});
|
|
await nextTick();
|
|
|
|
const q1 = wrapper.vm.questions[0];
|
|
wrapper.vm.handleAnswer(q1, "1|nextQuestion|2|No");
|
|
|
|
expect(wrapper.vm.currentQuestionNum).toBe(2);
|
|
expect(wrapper.vm.isQuestionVisible(wrapper.vm.questions[1])).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
function getHondaAccordWindshieldQuestions() {
|
|
return [
|
|
{
|
|
questionSequence: 1,
|
|
questionText:
|
|
"Is your vehicle equipped with a Heads-up Display which projects vehicle information, such as speed, onto the windshield?",
|
|
answers: [
|
|
{ answerResult: "FW04796", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "", answerText: "No", nextQuestionSequence: 2 },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 2,
|
|
questionText:
|
|
"Is your vehicle equipped with an auto-dimming rearview mirror which will darken automatically when a vehicles approaches from the rear at night?",
|
|
answers: [
|
|
{ answerResult: "FW04795", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "", answerText: "No", nextQuestionSequence: 3 },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 3,
|
|
questionText: "Is your vehicle equipped with a power moonroof?",
|
|
answers: [
|
|
{ answerResult: "FW04794", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "FW04793", answerText: "No", nextQuestionSequence: null },
|
|
],
|
|
},
|
|
];
|
|
}
|
|
|
|
function getCherokeeWindshieldQuestions() {
|
|
return [
|
|
{
|
|
questionSequence: 1,
|
|
questionText:
|
|
"Is your Cherokee the Overland edition which can be identified by having a wood and leather wrapped steering wheel?",
|
|
answers: [
|
|
{ answerResult: "", answerText: "Yes", nextQuestionSequence: 2 },
|
|
{ answerResult: "", answerText: "No", nextQuestionSequence: 3 },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 2,
|
|
questionText:
|
|
"Is your vehicle equipped with rain sensing wipers that adjust their speed automatically when it rains?",
|
|
answers: [
|
|
{ answerResult: "DW02270", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "DW02264", answerText: "No", nextQuestionSequence: null },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 3,
|
|
questionText:
|
|
"Is your vehicle equipped with rain sensing wipers that adjust their speed automatically when it rains?",
|
|
answers: [
|
|
{ answerResult: "DW02268", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "", answerText: "No", nextQuestionSequence: 4 },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 4,
|
|
questionText:
|
|
"Is your vehicle equipped with automatic climate control which will change the fan speed automatically in order to maintain a set temperature?",
|
|
answers: [
|
|
{ answerResult: "", answerText: "Yes", nextQuestionSequence: 5 },
|
|
{ answerResult: "", answerText: "No", nextQuestionSequence: 6 },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 5,
|
|
questionText: "Is your vehicle equipped with heated seats?",
|
|
answers: [
|
|
{ answerResult: "DW02104", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "DW02103", answerText: "No", nextQuestionSequence: null },
|
|
],
|
|
},
|
|
{
|
|
questionSequence: 6,
|
|
questionText: "Is your vehicle equipped with heated seats?",
|
|
answers: [
|
|
{ answerResult: "DW02102", answerText: "Yes", nextQuestionSequence: null },
|
|
{ answerResult: "DW02101", answerText: "No", nextQuestionSequence: null },
|
|
],
|
|
},
|
|
];
|
|
}
|
|
|
|
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,
|
|
index: 0,
|
|
};
|
|
mountOptions["attachTo"] = document.body;
|
|
|
|
const wrapper = shallowMount(questionChain, mountOptions);
|
|
|
|
return { wrapper };
|
|
}
|