diff --git a/.prettierrc b/.prettierrc index f1c8e7129..21209f49e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { - "tabWidth": 2, + "tabWidth": 4, "bracketSameLine": true } \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 83e6a5c70..ded486df9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,8 +28,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - // TODO KO - statements: 55, + statements: 85, // Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90 }, }, diff --git a/src/App.vue b/src/App.vue index 71b56feaf..c934ee244 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,16 +1,65 @@ + + diff --git a/src/common-components/base-input-button/base-input-button.spec.js b/src/common-components/base-input-button/base-input-button.spec.js new file mode 100644 index 000000000..190448df9 --- /dev/null +++ b/src/common-components/base-input-button/base-input-button.spec.js @@ -0,0 +1,539 @@ +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", () => { + test.todo("handleChange is called with valueToEmit"); + + describe("checkbox", () => { + test.todo("modelValue is null => valueToEmit is correct value"); + + test.todo( + "modelValue is undefined => valueToEmit is correct value" + ); + test.todo( + "modelValue is empty => valueToEmit is correct value" + ); + + test.todo( + "modelValue is not empty and does not contain this button's value => valueToEmit is correct value" + ); + test.todo( + "modelValue is not empty and does contain this button's value => valueToEmit is correct value" + ); + }); + + 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: '', + data() { + return { + myValue: "", + }; + }, + }; + const mountMockData = { + ...mockData, + propsData: { + // to get rid of some annoying warnings + groupName: "groupName", + modelValue: mockData.propsData?.isMultiSelect ? [] : "", + value: "5", + setLastValuePushedToGa: () => {}, + // should override the above if they exist + ...mockData.propsData, + }, + }; + + const wrapper = shouldShallowMount + ? shallowMount(baseInputButton, mountMockData) + : mount(baseInputButton, mountMockData); + + wrapper.vm.pushEventToGA = jest.fn(); + wrapper.vm.$route = { + query: {}, + }; + wrapper.vm.GaActions = {} + + return { wrapper }; +} + +function setupBaseInputButtonWrapper({ mockData = {} }) { + const baseInputButtonWrapper = { + components: { baseInputButton }, + template: + '
', + data() { + return { + myValue: "", + isMultiSelect: mockData.isMultiSelect, + }; + }, + + // const parentComponent = mount({ + // data() { + // return { + // value: "value1", + // } + // }, + // template: '
', + // components: { baseInputButton } + // }) + }; + + const wrapper = mount(baseInputButtonWrapper, {}); + + return { wrapper }; +} diff --git a/src/common-components/base-input-button/base-input-button.vue b/src/common-components/base-input-button/base-input-button.vue index a3d890234..8cd50278e 100644 --- a/src/common-components/base-input-button/base-input-button.vue +++ b/src/common-components/base-input-button/base-input-button.vue @@ -2,8 +2,9 @@