CSR-762 Update some tests, get GA working for inputs for keyboard navigation

This commit is contained in:
Katie 2022-09-27 15:50:49 -04:00
parent d4100f4d26
commit fff1aa1848
10 changed files with 205 additions and 371 deletions

View file

@ -12,6 +12,8 @@
:aria-required="isRequired"
:value="value"
:checked="isChecked"
@blur="handleBlur"
@focus="handleFocus"
@keypress.enter="handleEventAction('keypressSubmit', $event)"
@change="handleEventAction('change', $event)" />
@ -56,6 +58,7 @@ export default {
type: Boolean,
default: true,
},
isRequired: Boolean,
},
data() {
return {
@ -89,7 +92,10 @@ export default {
} else {
switch (eventType) {
case eventTypes.CLICK:
console.log("RADIO CLICK");
// falls through. Keep this comment for the linter
case eventTypes.KEYPRESS_SUBMIT:
console.log("RADIO KEYPRESS CLICK");
this.handleClick(e);
break;
case eventTypes.CHANGE:
@ -117,22 +123,61 @@ export default {
this.valueToEmit = this.value;
}
window.currentlySelectedValues =
window.currentlySelectedValues ?? {};
window.currentlySelectedValues[this.groupName] = this.value;
this.handleChange(this.valueToEmit);
this.pushClickEventToGA();
},
handleClick(e) {
this.handleSelectionChange(e);
// console.log("clicked/selected: ", this.valueToEmit)
if (this.isMultiSelect) {
this.pushClickEventToGA();
}
this.$emit("buttonClicked", this.valueToEmit);
},
pushClickEventToGA() {
pushClickEventToGA(value) {
console.log("PUSHING: ", {
route: this.$route.query[queryStrings.FMG_PAGE],
value: value ?? this.value?.toString(),
valueToLogType: this.valueToLogType,
});
window.firedGaClickEventValues = window.firedGaClickEventValues ?? {}
window.firedGaClickEventValues[window.lastFocusedInputGroup] = value ?? this.value
this.pushEventToGA(
this.$route.query[queryStrings.FMG_PAGE],
this.GaActions.CLICKED,
this.value?.toString(),
value?.toString ?? this.value?.toString(),
true,
this.valueToLogType
);
},
handleBlur() {
window.lastFocusedInputGroup = this.groupName;
window.wasLastFocusedInputMultiselect = this.isMultiSelect
},
handleFocus() {
console.log("FOCUS: ", {
currentlySelectedValues: window.currentlySelectedValues,
firedGaClickEventValues: window.firedGaClickEventValues,
lastFocusedInputGroup: window.lastFocusedInputGroup,
wasLastFocusedInputMultiselect: window.wasLastFocusedInputMultiselect,
groupName: this.groupName
})
if (
window.currentlySelectedValues &&
window.firedGaClickEventValues &&
window.lastFocusedInputGroup &&
!window.wasLastFocusedInputMultiselect &&
window.currentlySelectedValues[window.lastFocusedInputGroup] &&
window.lastFocusedInputGroup != this.groupName &&
window.firedGaClickEventValues[window.lastFocusedInputGroup] != window.currentlySelectedValues[window.lastFocusedInputGroup]
) {
// TODO MAKE SURE THIS IS FIRING THE RIGHT VALUE
this.pushClickEventToGA(window.currentlySelectedValues[window.lastFocusedInputGroup])
}
},
},
computed: {
isChecked() {

View file

@ -22,8 +22,7 @@
<legend
class="sr-only"
:data-focus-target="formatString(groupName)"
:id="formatString(groupName)"
tabindex="-1">
:id="formatString(groupName)">
{{ questionText }}
{{
isMultiSelect && answers && answers.length > 1
@ -51,6 +50,8 @@
:validationRules="validationRules"
:textPosition="textPosition"
:selectOnKeypress="selectOnKeypress"
@blur="handleBlur"
@focus="handleFocus"
@buttonClicked="handleAnswerChange" />
<!-- For nested questions -->
<transition name="fade" mode="out-in">
@ -123,6 +124,7 @@ export default {
data() {
return {
primaryValue: "",
lastFocusedInputGroup: "",
};
},
computed: {
@ -175,10 +177,6 @@ export default {
}
},
buttonsInfo() {
console.log({
answers: this.answers,
isArray: Array.isArray(this.answers),
});
// TODO KO temporary. It should always just be an array
return (Array.isArray(this.answers) ? this.answers : [])?.map(
(answer) => ({
@ -191,7 +189,8 @@ export default {
buttonImageId: answer.buttonImageId ?? answer.ImageId,
groupName: this.formatString(this.groupName),
value:
answer.value ?? (this.useTextForValue
answer.value ??
(this.useTextForValue
? answer.Text
: answer.Name ?? answer),
})
@ -225,6 +224,17 @@ export default {
this.$emit("buttonQuestionChange", primaryAnswerValue);
this.$emit("update:modelValue", primaryAnswerValue);
},
handleBlur() {
// this.lastFocusedInputGroup = this.groupName;
// window.lastFocusedInputGroup = this.groupName
// console.log("blur: ", this.groupName);
},
handleFocus() {
// console.log("focused: ", {
// groupName: this.groupName,
// lastFocusedInputGroup: this.lastFocusedInputGroup,
// });
},
},
components: {
listButton,

View file

@ -1,78 +1,73 @@
import { shallowMount } from "@vue/test-utils";
import windshieldChipCountQuestion from "@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true});
jest.mock(
"@/store",
() => {
return {};
},
{ virtual: true }
);
describe("windshield-chip-count-question.vue", () => {
test("Selected chip count is emitted upon selection.", async () => {
//Arrange
const { wrapper } = setupMocks({modelValueProp: ["One"]});
//Act
wrapper.setValue({ modelValue: ["Two"] });
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.selectedChipCountValues).toEqual(["One"]);
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Two"] }]);
//Arrange
const { wrapper } = setupMocks({ modelValueProp: 1 });
//Act
await wrapper.setData({ numberOfChips: 2 });
//Assert
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([2]);
});
});
});
describe("Windshield-chip-count-question.vue", () => {
test("Should display question and answers from api.", async () => {
//Arrange
const { wrapper } = setupMocks({modelValueProp: ["One"]});
//Act
wrapper.setProps({isAvailable: true});
wrapper.vm.updateSelectedValues = jest.fn();
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.updateSelectedValues).toBeCalled();
});
});
function setupMocks({
function setupMocks({
modelValueProp = ["Two"],
groupName = "WindshieldChipCountQuestion",
cmsQuestionText = "How many chips are we repairing?",
cmsAnswers = [{Name: "One"}, {Name: "Two"}, {Name: "Three"}],
cmsAnswers = [{ Name: "One" }, { Name: "Two" }, { Name: "Three" }],
dataFromStoreApi = [],
}) {
}) {
//Mock store
store.dispatch = jest.fn(() => dataFromStoreApi);
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
store.getters = {
vehicle: {
year: 2019,
make: "honda",
model: "civc",
style: "2 Door",
category: "CAR",
},
};
const mountOptions = getMountOptions({
store: {
dispatch: store.dispatch,
getters: store.getters,
},
store: {
dispatch: store.dispatch,
getters: store.getters,
},
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
methods: {
getCmsContent: jest.fn(),
},
};
mountOptions.propsData = {
modelValue: modelValueProp
modelValue: modelValueProp,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(windshieldChipCountQuestion, mountOptions);
//Mock CMS content
const cmsContent = {
groupName: groupName,
QuestionText: cmsQuestionText,
Answers: cmsAnswers,
groupName: groupName,
QuestionText: cmsQuestionText,
Answers: cmsAnswers,
};
const damageOptions = dataFromStoreApi;
return { wrapper, cmsContent, damageOptions };
}
}

View file

@ -6,22 +6,22 @@ import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true});
describe("windshield-damage-type-question.vue", () => {
test("Selected chip count is emitted upon selection.", async () => {
test("Selected windshield damage is emitted upon selection.", async () => {
//Arrange
const { wrapper } = setupMocks({modelValueProp: ["Repair"]});
const { wrapper } = setupMocks({modelValueProp: "Repair"});
//Act
wrapper.setValue({ modelValue: ["Replace"] });
wrapper.setValue({ modelValue: "Replace" });
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.selectedValues).toEqual(["Repair"]);
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Replace"] }]);
expect(wrapper.vm.selectedWindshieldDamageType).toEqual("Repair");
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: "Replace" }]);
});
});
function setupMocks({
modelValueProp = ["Two"],
modelValueProp = "",
groupName = "WindshieldDamageTypeQuestion",
cmsQuestionText = "What's your windshield damage?",
cmsAnswers = [{Name: "Repair"}, {Name: "Replace"}],

View file

@ -7,12 +7,16 @@ export default {
buttonLabel: [Number, String],
buttonLabelSubCopy: String,
buttonImage: String,
altText: String,
altText: {
type: String,
default: ""
},
textPosition: String,
screenReaderOnlyText: String,
valueToLogType: String,
validationRules: String,
isWide: Boolean
isWide: Boolean,
isRequired: Boolean
},
data() {
return {

View file

@ -4,7 +4,6 @@
buttonWrapperClasses="list-group list-button rounded-3 d-flex flex-column w-100 mb-2"
@buttonClicked="handleAnswerChange">
<div
tabindex="-1"
:aria-label="buttonLabel"
class="list-button-content d-flex flex-column justify-content-center py-3 px-4">
<span class="m-0" :class="textPosition">

View file

@ -1,12 +1,12 @@
import { shallowMount } from "@vue/test-utils";
import { mount } from "@vue/test-utils";
import listCard from "./list-card";
import { nextTick } from "vue";
import { GaActions } from "@/constants/analytics";
describe("list-card.vue", () => {
it("Should return input type checkbox if isMultiSelect is true", async () => {
it("Should return input type checkbox if isMultiSelect is true", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isMultiSelect: true,
buttonLabel: "Windshield",
@ -22,9 +22,9 @@ describe("list-card.vue", () => {
expect(input.attributes().type).toEqual("checkbox");
});
it("Should return primary label text", async () => {
it("Should return primary label text", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -40,9 +40,9 @@ describe("list-card.vue", () => {
expect(paragraph.text()).toEqual("Windshield");
});
it("Should return secondary (sub) label text", async () => {
it("Should return secondary (sub) label text", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -59,28 +59,9 @@ describe("list-card.vue", () => {
expect(paragraph.text()).toEqual("Test");
});
it("Should return value used for various text settings including the label 'for' and input id", async () => {
it("Should return input group name used for radio or checkbox", () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
buttonID: "List Card Checkbox",
groupID: "radio-demo-1",
groupName: "radio 1",
buttonImage: "windshield-damage.svg",
buttonLabelSubCopy: "Test",
},
});
// Assert
const label = wrapper.find("label");
expect(label.attributes().for).toEqual("List Card Checkbox");
});
it("Should return input group name used for radio or checkbox", async () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -97,9 +78,9 @@ describe("list-card.vue", () => {
expect(input.attributes().name).toEqual("radio 1");
});
it("Should return aria-required state", async () => {
it("Should return aria-required state", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -116,9 +97,9 @@ describe("list-card.vue", () => {
expect(input.attributes()["aria-required"]).toEqual("true");
});
it("Should return flex row classes if isWide is true", async () => {
it("Should return flex row classes if isWide is true", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -133,13 +114,16 @@ describe("list-card.vue", () => {
});
// Assert
const label = wrapper.find("label");
expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-row", "py-2", "ps-4", "pe-4"]);
const label = wrapper.find(".list-card-content");
expect(label.exists()).toBe(true);
const labelClasses = wrapper.vm.labelClasses;
expect(labelClasses).toContain("flex-row");
expect(label.classes()).toContain("flex-row");
});
it("Should return flex row classes if isWide is true and checkboxTop if buttonLabelSubCopy is true", async () => {
it("Should return flex row classes if isWide is true and checkboxTop if buttonLabelSubCopy is provided", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -154,13 +138,18 @@ describe("list-card.vue", () => {
});
// Assert
const label = wrapper.find("label");
expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-row", "py-2", "ps-4", "pe-4", "checkboxTop"]);
const label = wrapper.find(".list-card-content");
expect(label.exists()).toBe(true);
const labelClasses = wrapper.vm.labelClasses;
expect(labelClasses).toContain("flex-row");
expect(label.classes()).toContain("flex-row");
expect(labelClasses).toContain("checkboxTop");
expect(label.classes()).toContain("checkboxTop");
});
it("Should return flex column classes if isWide is false", async () => {
it("Should return flex column classes if isWide is false", () => {
// Act
const wrapper = shallowMount(listCard, {
const wrapper = mount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
@ -174,161 +163,10 @@ describe("list-card.vue", () => {
});
// Assert
const label = wrapper.find("label");
expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-column", "pt-4", "pb-3"]);
const label = wrapper.find(".list-card-content");
expect(label.exists()).toBe(true);
const labelClasses = wrapper.vm.labelClasses;
expect(labelClasses).toContain("flex-column");
expect(label.classes()).toContain("flex-column");
});
it("Should emit button value on click", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
value: "List Card Checkbox",
groupID: "radio-demo-1",
groupName: "radio 1",
buttonImage: "windshield-damage.svg",
isRequired: true,
isWide: false,
buttonID: 'list-card-id',
selectedValues: "List Card Checkbox"
},
});
wrapper.vm.handleCheckChange();
// Assert
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: true, buttonId: 'list-card-id'}]);
});
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
isRadioHorizontal: true,
buttonLabel: "Windshield",
value: "List Card Checkbox",
groupID: "radio-demo-1",
groupName: "radio 1",
buttonImage: "windshield-damage.svg",
isRequired: true,
isWide: false,
selectedValues: "Car-Front"
},
});
// Assert
expect(wrapper.componentVM.checkValue).toEqual(false);
});
it("Should set an initial value for validation if selectedValues include the value", async () => {
// Arrange
const wrapper = shallowMount(listCard, {
propsData: {
value: "Windshield",
groupName: "radio 1",
selectedValues: ["Windshield"],
},
});
// Assert
expect(wrapper.vm.fieldOptions.initialValue).toEqual([ 'Windshield' ]);
});
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
selectingInitiatesLoad: false,
},
});
// Assert
wrapper.vm.handleInputChange();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
it("Should do nothing if isMultiSelect is true and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
isMultiSelect: true,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleKeyupArrow).toHaveReturned;
});
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
selectingInitiatesLoad: false,
isMultiSelect: false,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
it("Should run handleChange if triggerButton is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
global: {
mocks: {
'$route': { query: { fmgPage: 'page-name' } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
}
},
propsData: {
selectingInitiatesLoad: false,
},
});
// Assert
wrapper.vm.triggerButton();
await nextTick();
expect(wrapper.vm.handleChange).toBeCalled;
expect(wrapper.vm.handleCheckChange).not.toBeCalled;
expect(wrapper.vm.displayLoader).not.toBeCalled;
});
it("Should run handleCheckChange and displayLoader if triggerButton is triggered and seletingInitiatesLoad is true", async () => {
// Act
const wrapper = shallowMount(listCard, {
global: {
mocks: {
'$route': { query: { fmgPage: 'page-name' } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
}
},
propsData: {
selectingInitiatesLoad: true,
},
});
// Assert
wrapper.vm.triggerButton();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
expect(wrapper.vm.displayLoader).toBeCalled;
});
});

View file

@ -8,7 +8,7 @@
@buttonClicked="handleAnswerChange">
<div
class="d-flex w-100 align-items-center px-2 h-100 list-card-content"
:class="getLabelClasses">
:class="labelClasses">
<img
:id="buttonImageId"
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
@ -46,7 +46,7 @@ export default {
baseInputButton,
},
computed: {
getLabelClasses() {
labelClasses() {
if (this.isWide) {
let classes = "flex-row py-2 ps-4 pe-4";
if (this.buttonLabelSubCopy) {

View file

@ -1,117 +1,60 @@
import { shallowMount } from "@vue/test-utils";
import { mount } from "@vue/test-utils";
import radio from "./radio";
import { nextTick } from "vue";
import { GaActions } from "@/constants/analytics";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
describe("radio.vue", () => {
it("Should return group name", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
groupName: "radio-button-test",
},
it("Should have correct group name", async () => {
// Arrange
let { wrapper } = setupMocks({});
// Act
await wrapper.setProps({
groupName: "radio-button-test",
});
const input = wrapper.find("input");
// Assert
expect(input.attributes().name).toEqual("radio-button-test");
});
// Assert
const input = wrapper.find("input");
it("Should have correct label text", async () => {
// Act
let { wrapper } = setupMocks({});
// Expect
expect(input.attributes().name).toEqual("radio-button-test");
});
// Arrange
await wrapper.setProps({
buttonLabel: "label text",
});
const paragraph = wrapper.find("p");
it("Should return checkbox id", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
buttonID: "Radio ID",
},
// Assert
expect(paragraph.text()).toEqual("label text");
});
// Assert
const input = wrapper.find("input");
it("Should have correct screenreader-only text", async () => {
// Act
let { wrapper } = setupMocks({});
// Expect
expect(input.attributes().id).toEqual("Radio ID");
});
// Arrange
await wrapper.setProps({
screenReaderOnlyText: "screenreader text",
});
const paragraph = wrapper.find(".sr-only");
it("Should return label text", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
buttonLabel: "label text",
},
// Assert
expect(paragraph.text()).toEqual("screenreader text");
});
// Assert
const paragraph = wrapper.find("p");
expect(paragraph.text()).toEqual("label text");
});
it("Should return label text", async () => {
// Act
const wrapper = shallowMount(radio, {
propsData: {
screenReaderOnlyText: "screenreader text",
},
});
// Assert
const paragraph = wrapper.find("span");
expect(paragraph.text()).toEqual("screenreader text");
});
it("Should emit button value on click", async () => {
// Act
const wrapper = shallowMount(radio, {
global: {
mocks: {
'$route': { query: { fmgPage: 'page-name' } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
}
},
propsData: {
buttonLabel: "Windshield",
value: "List Card Checkbox",
buttonID: "List Card Checkbox",
groupID: "radio-demo-1",
groupName: "radio 1",
isRequired: true,
isWide: false,
modelValue: ["List Card Checkbox"],
},
});
wrapper.vm.handleCheckChange();
// Assert
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: false}]);;
expect(wrapper.vm.pushEventToGA).toHaveBeenCalled();
});
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
// Act
const wrapper = shallowMount(radio, {
global: {
mocks: {
'$route': { query: { fmgPage: 'page-name' } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
}
},
propsData: {
buttonLabel: "Windshield",
buttonID: "List Card Checkbox",
groupID: "radio-demo-1",
groupName: "radio 1",
buttonImage: "windshield-damage.svg",
isRequired: true,
modelValue: ["List Card Checkbox"],
value: "Car-Front",
selectedValues: "Car-Front"
},
});
// Assert
expect(wrapper.componentVM.checkValue).toEqual(true);
});
});
function setupMocks({ mountOptionsMockData = {} }) {
const wrapper = mount(
radio,
getMountOptions({
...mountOptionsMockData,
mixins: [inputButtonWrapperMixin],
})
);
return { wrapper };
}

View file

@ -4,7 +4,7 @@
buttonWrapperClasses="ui-radio form-check"
inputClasses="form-check-input"
@buttonClicked="handleAnswerChange">
<div class="d-flex align-items-start form-check-label" :for="buttonID">
<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">{{
screenReaderOnlyText