import { mount } from "@vue/test-utils"; import waitlistQuestion from "./waitlist-question"; import store from "@/store"; import { experimentSettings } from "@/constants/experiments"; jest.mock("@/store", () => ({ getters: { applicationUser: { experiments: [], }, }, })); const MOCK_CMS_CONTENT = { WaitListLabelWidget: { Text: "Want to be notified sooner?" }, WaitListQuestionWidget: { QuestionText: "Add me to the waitlist" }, }; function dateStringOffsetFromToday(offsetDays) { const d = new Date(); d.setDate(d.getDate() + offsetDays); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String( d.getDate() ).padStart(2, "0")}`; } function enableWaitlistExperiment(thresholdDays = 0) { store.getters.applicationUser.experiments = [ { isActive: true, settings: { [experimentSettings.DISPLAY_WAITLIST]: "true", [experimentSettings.WAITLIST_THRESHOLD_DAYS]: String(thresholdDays), }, }, ]; } function mountComponent(props = {}) { const cmsMixin = { methods: { getCmsContent: jest.fn((widgetName, fieldName) => { return MOCK_CMS_CONTENT[widgetName]?.[fieldName] ?? ""; }), }, }; return mount(waitlistQuestion, { props: { modelValue: false, availableDates: [dateStringOffsetFromToday(10)], ...props, }, global: { mixins: [cmsMixin], }, }); } describe("waitlist-question.vue", () => { beforeEach(() => { enableWaitlistExperiment(); }); afterEach(() => { store.getters.applicationUser.experiments = []; }); it("renders the label and checkbox CMS content", () => { const wrapper = mountComponent(); expect(wrapper.text()).toContain("Want to be notified sooner?"); expect(wrapper.text()).toContain("Add me to the waitlist"); }); it("reflects the modelValue prop on the checkbox", () => { const wrapper = mountComponent({ modelValue: true }); expect(wrapper.find("input[type='checkbox']").element.checked).toBe(true); }); it("emits update:modelValue with true when the checkbox is checked", async () => { const wrapper = mountComponent({ modelValue: false }); const input = wrapper.find("input[type='checkbox']"); await input.setValue(true); expect(wrapper.emitted("update:modelValue")).toEqual([[true]]); }); it("emits update:modelValue with false when the checkbox is unchecked", async () => { const wrapper = mountComponent({ modelValue: true }); const input = wrapper.find("input[type='checkbox']"); await input.setValue(false); expect(wrapper.emitted("update:modelValue")).toEqual([[false]]); }); it("renders nothing when shouldDisplay is false", () => { store.getters.applicationUser.experiments = []; const wrapper = mountComponent(); expect(wrapper.find("input[type='checkbox']").exists()).toBe(false); }); describe("clicking the container", () => { it("toggles localValue to true when clicking outside the checkbox", async () => { const wrapper = mountComponent({ modelValue: false }); await wrapper.find(".waitlist-label").trigger("click"); expect(wrapper.emitted("update:modelValue")).toEqual([[true]]); }); it("toggles localValue to false when clicking outside the checkbox", async () => { const wrapper = mountComponent({ modelValue: true }); await wrapper.find(".waitlist-question").trigger("click"); expect(wrapper.emitted("update:modelValue")).toEqual([[false]]); }); it("does not toggle when the click target is the checkbox input itself", () => { // jsdom doesn't reliably run a checkbox's native activation behavior // (toggling + firing "change") for script-dispatched clicks, so this // calls the handler directly with the real input element as the // event target to verify the guard is skipped in that case. const wrapper = mountComponent({ modelValue: false }); const inputElement = wrapper.find("input[type='checkbox']").element; wrapper.vm.handleContainerClick({ target: inputElement }); expect(wrapper.emitted("update:modelValue")).toBeUndefined(); }); it("does not toggle when the click lands inside the checkbox wrapper but not on the input", async () => { const wrapper = mountComponent({ modelValue: false }); await wrapper.find(".ui-checkbox").trigger("click"); expect(wrapper.emitted("update:modelValue")).toBeUndefined(); }); }); describe("shouldDisplay", () => { it("is false when the DISPLAY_WAITLIST experiment is off", () => { store.getters.applicationUser.experiments = [ { isActive: true, settings: { [experimentSettings.WAITLIST_THRESHOLD_DAYS]: "3" }, }, ]; const wrapper = mountComponent({ availableDates: [dateStringOffsetFromToday(10)] }); expect(wrapper.vm.shouldDisplay).toBe(false); }); it("is false when the earliest available date is within the threshold", () => { enableWaitlistExperiment(3); const wrapper = mountComponent({ availableDates: [dateStringOffsetFromToday(2)] }); expect(wrapper.vm.shouldDisplay).toBe(false); }); it("is true when the experiment is on and the earliest available date exceeds the threshold", () => { enableWaitlistExperiment(3); const wrapper = mountComponent({ availableDates: [dateStringOffsetFromToday(10)] }); expect(wrapper.vm.shouldDisplay).toBe(true); }); it("is false when there are no available dates", () => { enableWaitlistExperiment(0); const wrapper = mountComponent({ availableDates: [] }); expect(wrapper.vm.shouldDisplay).toBe(false); }); }); });