185 lines
6.1 KiB
JavaScript
185 lines
6.1 KiB
JavaScript
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);
|
|
});
|
|
});
|
|
});
|