CSR-762 Add tests

This commit is contained in:
Katie 2022-10-05 14:22:25 -04:00
parent f634d8ced1
commit fe9602200c
9 changed files with 656 additions and 32 deletions

View file

@ -0,0 +1,523 @@
import { shallowMount, mount } from "@vue/test-utils";
import baseInputButton from "./base-input-button";
describe("baseInputButton.vue", () => {
describe("general", () => {
describe("checkbox", () => {
test("isMultiSelect => baseInputButton is a checkbox", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(inputElement.attributes().type).toEqual("checkbox");
});
});
describe("radio", () => {
test("!isMultiSelect => baseInputButton is a radio button", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(inputElement.attributes().type).toEqual("radio");
});
});
// tests in here should be test.each
describe("shared", () => {
const isMultiSelectOptions = [true, false];
test.each(isMultiSelectOptions)("groupName", (isMultiSelect) => {
const { wrapper } = setupMocks({
mockData: {
propsData: {
groupName: "boogly",
isMultiSelect: isMultiSelect,
},
},
});
// Assert
const inputElement = wrapper.find("input");
const inputElementAttributes = inputElement.attributes();
expect(inputElementAttributes.name).toEqual("boogly");
});
});
});
describe("mouse clicks", () => {
describe("checkbox", () => {
test("clicked => correct event and value are emitted", async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
value: "X",
},
},
});
// Act
await wrapper.trigger("mousedown.left");
await wrapper.trigger("click");
// Assert
expect(wrapper.emitted()["inputButtonClicked"][0][0]).toEqual([
"X",
]);
});
});
describe("radio", () => {
test("clicked => correct event and value are emitted", async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
value: "X",
},
},
});
// Act
await wrapper.trigger("mousedown.left");
await wrapper.trigger("click");
// Assert
expect(wrapper.emitted()["inputButtonClicked"][0][0]).toEqual(
"X"
);
});
});
});
describe("keyboard navigation and events", () => {
describe("checkbox", () => {
test.todo(
"focus on a checkbox => inputButtonClicked is not emitted"
);
test.todo(
"blur from a checkbox => inputButtonClicked is not emitted"
);
test("change event fired from checkbox => inputButtonClicked is emitted with correct value", async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
value: "Hi",
},
},
});
const input = wrapper.find("input");
// Act
await input.trigger("change");
// Assert
expect(wrapper.emitted()["inputButtonClicked"][0][0]).toEqual([
"Hi",
]);
});
test("focus and click space on a checkbox => inputButtonClicked is not emitted", async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
value: "Hi",
},
},
});
const input = wrapper.find("input");
// Act
await input.trigger("keypress", { key: "space" });
// Assert
expect(wrapper.emitted()).not.toHaveProperty(
"inputButtonClicked"
);
});
test("focus and click enter on a checkbox => inputButtonClicked is emitted with correct value", async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
value: "Hi",
},
},
});
const input = wrapper.find("input");
// Act
await input.trigger("keypress", { key: "enter" });
// Assert
expect(wrapper.emitted()["inputButtonClicked"][0][0]).toEqual([
"Hi",
]);
});
});
describe("radio", () => {
test.todo(
"focus on a radio button => inputButtonClicked is not emitted"
);
test.todo(
"blur from a radio button => inputButtonClicked is not emitted"
);
test("focus and click space on a radio button => inputButtonClicked is emitted with correct value", async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
value: "Hi",
},
},
});
const input = wrapper.find("input");
// Act
await input.trigger("keypress", { key: "space" });
// Assert
expect(wrapper.emitted()).toHaveProperty("inputButtonClicked");
expect(wrapper.emitted()["inputButtonClicked"][0][0]).toEqual(
"Hi"
);
});
test(
"focus and click enter on a radio button => inputButtonClicked is emitted with correct value",
async () => {
// Arrange
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
value: "Hi",
},
},
});
const input = wrapper.find("input");
// Act
await input.trigger("keypress", { key: "enter" });
// Assert
expect(wrapper.emitted()).toHaveProperty(
"inputButtonClicked"
);
expect(
wrapper.emitted()["inputButtonClicked"][0][0]
).toEqual("Hi");
}
);
});
});
describe("methods", () => {
describe("handleEventAction", () => {});
describe("handleSelectionChange", () => {
describe("checkbox", () => {
test.todo("modelValue is null => inputButtonClicked is emitted with correct")
})
describe("radio", () => {
})
});
describe("handleClick", () => {
test.todo("handleSelectChange is also called")
test.todo("inputButtonClicked is emitted with valueToEmit")
});
});
describe("computed", () => {
describe("isChecked", () => {
describe("checkbox", () => {
const falsyModelValues = [[], null, undefined];
test.each(falsyModelValues)(
"modelValue is falsy/empty => checkbox isn't checked",
(modelValue) => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
modelValue,
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.isChecked).toEqual(false);
expect(inputElement.element.checked).toBe(false);
}
);
test("modelValue doesn't contain this button's value => checkbox isn't checked", async () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
modelValue: ["Aaa", "Bbb", "Ccc"],
value: "Ddd",
},
},
});
await wrapper.vm.$nextTick();
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.isChecked).toEqual(false);
expect(inputElement.element.checked).toBe(false);
});
test("modelValue contains this button's value => checkbox is checked", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
modelValue: ["Aaa", "Bbb", "Ddd", "Ccc"],
value: "Ddd",
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.isChecked).toEqual(true);
expect(inputElement.element.checked).toBe(true);
});
});
describe("radio", () => {
const falsyModelValues = ["", null, undefined, []];
test.each(falsyModelValues)(
"modelValue is falsy/empty => radio button isn't checked",
(modelValue) => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
modelValue,
value: "Aaa",
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.isChecked).toEqual(false);
expect(inputElement.element.checked).toBe(false);
}
);
test("modelValue equals this button's value => radio button is checked", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
modelValue: "Ddd",
value: "Ddd",
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.isChecked).toEqual(true);
expect(inputElement.element.checked).toBe(true);
});
test("modelValue doesn't equal this button's value => radio button isn't checked", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
modelValue: "Aaa",
value: "Ddd",
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.isChecked).toEqual(false);
expect(inputElement.element.checked).toBe(false);
});
});
});
describe("buttonId", () => {
test("groupName and value combo yield correct id for input button with string value", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
groupName: "my test-name",
value: "Aaa-BBB CcC",
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.buttonId).toBe("my-test-name-Aaa-BBB-CcC");
expect(inputElement.attributes().id).toBe(
"my-test-name-Aaa-BBB-CcC"
);
});
test("groupName and value combo yield correct id for input button with number value", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
groupName: "my test-name",
value: 2,
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.buttonId).toBe("my-test-name-2");
expect(inputElement.attributes().id).toBe("my-test-name-2");
});
});
describe("inputType", () => {
test("isMultiSelect is true => inputType is 'checkbox'", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: true,
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.inputType).toEqual("checkbox");
expect(inputElement.attributes().type).toBe("checkbox");
});
test("isMultiSelect is false => inputType is 'radio'", () => {
// Arrange/Act
const { wrapper } = setupMocks({
mockData: {
propsData: {
isMultiSelect: false,
},
},
});
// Assert
const inputElement = wrapper.find("input");
expect(wrapper.vm.inputType).toEqual("radio");
expect(inputElement.attributes().type).toBe("radio");
});
});
});
});
// TODO KO look at how I tested groups of these in SFA (making sure selecting one radio changes the value, etc, that this acts like a regular input aside from a different emitted event)
function setupMocks({ mockData = {}, shouldShallowMount = true }) {
const baseInputButtonWrapper = {
components: { baseInputButton },
template: '<baseInputButton v-model="myValue" />',
data() {
return {
myValue: "",
};
},
};
const mountMockData = {
...mockData,
propsData: {
// to get rid of some annoying warnings
groupName: "groupName",
modelValue: mockData.propsData?.isMultiSelect ? [] : "",
value: "5",
// should override the above if they exist
...mockData.propsData,
},
};
const wrapper = shouldShallowMount
? shallowMount(baseInputButton, mountMockData)
: mount(baseInputButton, mountMockData);
return { wrapper };
}
function setupBaseInputButtonWrapper({ mockData = {} }) {
const baseInputButtonWrapper = {
components: { baseInputButton },
template:
'<div><baseInputButton v-model="myValue" groupName="myGroupName" value="X" :isMultiSelect="isMultiSelect" /></div>',
data() {
return {
myValue: "",
isMultiSelect: mockData.isMultiSelect,
};
},
// const parentComponent = mount({
// data() {
// return {
// value: "value1",
// }
// },
// template: '<div><baseInputButton v-model="value" value="value1" /><baseInputButton v-model="value" value="value2" /></div>',
// components: { baseInputButton }
// })
};
const wrapper = mount(baseInputButtonWrapper, {});
return { wrapper };
}

View file

@ -21,14 +21,14 @@
</template>
<script>
// import { queryStrings } from "@/constants/query-strings";
import { useField } from "vee-validate";
import { toRef } from "vue";
// import { gaStoreActions } from "../../constants/store-actions";
import { queryStrings } from "@/constants/query-strings";
import eventBus from "@/helpers/event-bus/event-bus.js";
export default {
name: "base-input-button",
emits: ["change"],
emits: ["change", "inputButtonClicked"],
model: {
prop: "modelValue",
event: "change",
@ -79,6 +79,8 @@ export default {
CLICK: "click",
};
console.log("Handling: ", eventType)
if (this.isMultiSelect) {
switch (eventType) {
case eventTypes.ENTER:
@ -89,7 +91,7 @@ export default {
// this.groupName,
// false
// );
// this.pushClickEventToGA();
break;
}
@ -138,12 +140,20 @@ export default {
},
handleClick(e) {
this.handleSelectionChange(e);
this.$emit("buttonClicked", this.valueToEmit);
// console.log("addEventToBus: ", this.value?.toString())
// eventBus.addEventToBus("GA", "click", {
// category: this.$route.query[queryStrings.FMG_PAGE],
// action: this.GaActions.CLICKED,
// label: this.value?.toString(),
// pushToLogApp: true,
// valueToLogType: this.valueToLogType,
// });
this.$emit("inputButtonClicked", this.valueToEmit);
},
// pushClickEventToGA(value) {
// const lastFocusedInputGroup =
// this.$store.getters.gaClickInformation.lastFocusedInputGroup;
// // this.dispatchStoreAction(
// // this.disA MKJ `,,.patchStoreAction(
// // gaStoreActions.UPDATE_FIRED_GA_CLICK_EVENT_VALUES,
// // {
// // groupName: lastFocusedInputGroup,
@ -213,7 +223,7 @@ export default {
// // this.groupName,
// // false
// // );
// if (
// firedGaClickEventValues[this.groupName] !=
// currentlySelectedValues[this.groupName]
@ -221,7 +231,7 @@ export default {
// this.pushClickEventToGA(
// currentlySelectedValues[this.groupName]
// );
// }
// }
// }
// },
},
@ -229,18 +239,19 @@ export default {
isChecked() {
if (this.isMultiSelect && this.modelValue instanceof Array) {
return this.modelValue.includes(this.value);
} else if (!this.isMultiSelect) {
return this.modelValue == this.value;
} else {
return false;
}
return this.modelValue == this.value;
},
inputType() {
return this.isMultiSelect ? "checkbox" : "radio";
},
buttonId() {
return `${this.groupName}-${JSON.stringify(this.value)?.replace(
" ",
"-"
)}`;
return `${this.groupName?.replace(" ", "-")}-${this.value
.toString()
?.replace(" ", "-")}`;
},
},
setup(props) {

View file

@ -54,7 +54,7 @@
:validationRules="validationRules"
:textPosition="textPosition"
:selectOnKeypress="selectOnKeypress"
@buttonClicked="handleAnswerChange" />
@inputButtonClicked="handleAnswerChange" />
<!-- For nested questions -->
<transition name="fade" mode="out-in">
<div
@ -84,6 +84,7 @@ import listCard from "@/ux-components/list-card/list-card";
import radio from "@/ux-components/radio/radio";
import { ErrorMessage } from "vee-validate";
import { queryStrings } from "@/constants/query-strings";
import eventBus from "../../helpers/event-bus/event-bus";
export default {
name: "buttonQuestion",
@ -229,26 +230,29 @@ export default {
handleAnswerChange(primaryAnswerValue) {
this.primaryValue = primaryAnswerValue;
this.pushClickEventToGA(null, primaryAnswerValue);
this.pushClickEventToGA(primaryAnswerValue);
this.$emit("buttonQuestionChange", primaryAnswerValue);
this.$emit("update:modelValue", primaryAnswerValue);
},
pushClickEventToGA(e, value) {
const valueToPushToGa =
value?.toString() ?? this.primaryValue?.toString();
pushClickEventToGA() {
// const valueToPushToGa =
// value?.toString() ?? this.primaryValue?.toString();
const event = eventBus.readAndPopEventFromBus("GA", "click")
const valueToPushToGa = event.label;
console.log({
valueToPushToGa,
lastPushedGaEventValue: this.lastPushedGaEventValue,
});
if (valueToPushToGa !== this.lastPushedGaEventValue) {
if (valueToPushToGa && valueToPushToGa !== this.lastPushedGaEventValue) {
this.lastPushedGaEventValue = valueToPushToGa;
this.pushEventToGA(
this.$route.query[queryStrings.FMG_PAGE],
this.GaActions.CLICKED,
event.category,
event.action,
valueToPushToGa,
true,
this.valueToLogType
event.pushToLogType,
event.valueToLogType
);
}
},

View file

@ -38,9 +38,14 @@ export default {
this.$emit("change", e);
},
},
watch: {
selectedValue(selectedValue) {
this.$emit("update:modelValue", selectedValue);
},
computed: {
selectedValue: {
get: () => {
return this.modelValue;
},
set: function(newValue) {
this.$emit("update:modelValue", newValue);
}
}
},
};

View file

@ -0,0 +1,81 @@
describe("input-button-wrapper-mixin", () => {
describe("mouse clicks", () => {
describe("checkbox", () => {
test.todo("clicking once checks the baseInputButton");
test.todo("clicking twice unchecks the baseInputButton");
test.todo(
"is initially checked => checking unchecks the baseInputButton"
);
test.todo("clicked => correct event and value are emitted");
});
describe("radio", () => {
test.todo("clicking once selects the baseInputButton");
test.todo("clicking twice keeps the baseInputButton selected");
test.todo(
"is initially selected => click keeps the baseInputButton selected"
);
test.todo("clicked => correct event and value are emitted");
});
});
describe("keyboard navigation and", () => {
describe("checkbox", () => {
test.todo(
"focus on a checkbox => inputButtonClicked is not emitted"
);
test.todo(
"blur from a checkbox => inputButtonClicked is not emitted"
);
test.todo(
"focus and click space on a checkbox => inputButtonClicked is emitted with correct value"
);
test.todo(
"focus and click space on a checkbox that is already checked => inputButtonClicked is emitted with correct value"
);
test.todo(
"focus and click enter on a checkbox => inputButtonClicked is emitted with correct value"
);
test.todo(
"focus and click enter on a checkbox that is already checked => inputButtonClicked is emitted with correct value"
);
});
describe("radio", () => {
test.todo(
"focus on a radio button => inputButtonClicked is not emitted"
);
test.todo(
"blur from a radio button => inputButtonClicked is not emitted"
);
test.todo(
"focus and click space on a radio button => inputButtonClicked is emitted with correct value"
);
test.todo(
"focus and click space on a radio button that is already selected => inputButtonClicked is emitted with correct value"
);
test.todo(
"focus and click enter on a radio button => inputButtonClicked is emitted with correct value"
);
test.todo(
"focus and click enter on a radio button that is already selected => inputButtonClicked is emitted with correct value"
);
});
});
});

View file

@ -5,7 +5,7 @@
'list-group list-button-horizontal d-flex flex-column w-100',
{ 'radio-fancy': isCashOrInsurance },
]"
@buttonClicked="handleAnswerChange">
@inputButtonClicked="handleAnswerChange">
<div
class="list-button-horizontal-content d-flex flex-column justify-content-center p-3">
<span class="m-0" :class="textPosition">

View file

@ -2,7 +2,7 @@
<baseInputButton
v-bind="$props"
buttonWrapperClasses="list-group list-button rounded-3 d-flex flex-column w-100 mb-2"
@buttonClicked="handleAnswerChange">
@inputButtonClicked="handleAnswerChange">
<div
:aria-label="buttonLabel"
class="list-button-content d-flex flex-column justify-content-center py-3 px-4">

View file

@ -5,7 +5,7 @@
'list-card w-100 rounded-3 d-flex align-items-center h-100',
{ horizontal: isWide },
]"
@buttonClicked="handleAnswerChange">
@inputButtonClicked="handleAnswerChange">
<div
class="d-flex w-100 align-items-center px-2 h-100 list-card-content"
:class="labelClasses">

View file

@ -3,7 +3,7 @@
v-bind="$props"
buttonWrapperClasses="ui-radio form-check"
inputClasses="form-check-input"
@buttonClicked="handleAnswerChange">
@inputButtonClicked="handleAnswerChange">
<div class="d-flex align-items-start form-check-label">
<p v-if="buttonLabel" class="m-0">{{ buttonLabel }}</p>
<span v-if="screenReaderOnlyText" class="sr-only">{{