import { shallowMount, mount } from '@vue/test-utils'; import inputButtonWrapperMixin from '@/mixins/input-button-wrapper-mixin'; import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue'; const buttonLabelReference = 'span[id="buttonLabelSpan"]'; const buttonLabelCopyReference = 'span[id="buttonLabelSubCopySpan"]'; const availabilityIndicatorBlockReference = 'div[id="availabilityIndicatorBlock"]'; const loaderReference = { ref: 'availabilityIndicatorLoader' }; const availabilityBadgeReference = 'div[id="availabilityBadge"]'; const buttonBodyCopyReference = 'span[id="buttonBodyCopy"]'; const screenReaderOnlySpanReference = '#screenReaderOnlyTextSpan'; describe('Shop list button', () => { describe('should render', () => { test('correctly with required props', async () => { // Arrange const wrapper = shallowMount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group' }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Assert expect(wrapper.html()).toMatchSnapshot(); }); test('correctly with all relevant props', async () => { const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(null) }; const wrapper = shallowMount(shopListButton, { propsData: { buttonLabel: 'Label of button', buttonLabelSubCopy: 'Sub copy of button', selectedValue: 'selected value', buttonBodyCopy: 'button body copy', screenReaderOnlyText: 'screen reader only text', value: 1234, modelValue: 'value of modal', groupName: 'name of group', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Assert expect(wrapper.html()).toMatchSnapshot(); }); test('expected data with only required props', async () => { // Arrange const wrapper = shallowMount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group' }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Assert expect(wrapper.vm.$data).toMatchSnapshot(); }); test('expected data with all relevant props', async () => { // Arrange const wrapper = shallowMount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', additionalButtonData: { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(null) } }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Assert expect(wrapper.vm.$data).toMatchSnapshot(); }); describe('button label with expected classes', () => { test('when "textPosition" prop provided', async () => { // Arrange const textPosition = 'positionOfText'; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', textPosition }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const buttonLabel = wrapper.find(buttonLabelReference); // Assert expect(buttonLabel.exists()).toBeTruthy(); expect(buttonLabel.classes()).toContain(textPosition); }); test('when "textPosition" prop not provided', async () => { // Arrange const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button' }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const buttonLabel = wrapper.find(buttonLabelReference); // Assert expect(buttonLabel.exists()).toBeTruthy(); }); }); describe('button label sub copy with expected classes', () => { test('when "textPosition" prop provided', async () => { // Arrange const textPosition = 'positionOfText'; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', textPosition }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const buttonLabelSubCopy = wrapper.find(buttonLabelCopyReference); // Assert expect(buttonLabelSubCopy.exists()).toBeTruthy(); expect(buttonLabelSubCopy.classes().length).toBe(4); expect(buttonLabelSubCopy.classes()).toContain('m-0'); expect(buttonLabelSubCopy.classes()).toContain('caption'); expect(buttonLabelSubCopy.classes()).toContain('ms-2'); expect(buttonLabelSubCopy.classes()).toContain(textPosition); }); test('when "textPosition" prop not provided', async () => { // Arrange const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button' }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const buttonLabelSubCopy = wrapper.find(buttonLabelCopyReference); // Assert expect(buttonLabelSubCopy.exists()).toBeTruthy(); expect(buttonLabelSubCopy.classes().length).toBe(3); expect(buttonLabelSubCopy.classes()).toContain('m-0'); expect(buttonLabelSubCopy.classes()).toContain('caption'); expect(buttonLabelSubCopy.classes()).toContain('ms-2'); }); }); describe('availability indicator block with expected when displayAvailabilityIndicators true', () => { test('and availability rating is high', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve('high') }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference); // Assert expect(availabilityIndicatorBlock.exists()).toBeTruthy(); expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator'); expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill'); expect(availabilityIndicatorBlock.classes()).toContain('green'); }); test.each(['low', ''])( 'and availability rating is not high or null', async (availabilityBadge) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityBadge) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference); // Assert expect(availabilityIndicatorBlock.exists()).toBeTruthy(); expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator'); expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill'); expect(availabilityIndicatorBlock.classes()).toContain('orange'); } ); test.each([null, undefined])( 'and availability rating is null', async (availabilityRating) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference); // Assert expect(availabilityIndicatorBlock.exists()).toBeTruthy(); expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator'); expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill'); expect(availabilityIndicatorBlock.classes()).toContain('gray'); } ); }); test('availability indicator block with expected classes when displayAvailabilityIndicators true', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const availabilityIndicatorBlock = wrapper.find(availabilityIndicatorBlockReference); // Assert expect(availabilityIndicatorBlock.exists()).toBeTruthy(); expect(availabilityIndicatorBlock.classes()).toContain('availability-indicator'); expect(availabilityIndicatorBlock.classes()).toContain('rounded-pill'); }); describe('availability badge when displayAvailabilityIndicators true', () => { test('and "availabilityRating" is "high"', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve('high') }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const availabilityBadge = wrapper.find(availabilityBadgeReference); // Assert expect(availabilityBadge.exists()).toBeTruthy(); expect(availabilityBadge.classes().length).toBe(2); expect(availabilityBadge.classes()).toContain('availability-badge'); expect(availabilityBadge.classes()).toContain('green'); }); test.each(['low', ''])( 'and "availabilityRating" is not "high" or null', async (availabilityRating) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const availabilityBadge = wrapper.find(availabilityBadgeReference); // Assert expect(availabilityBadge.exists()).toBeTruthy(); expect(availabilityBadge.classes().length).toBe(2); expect(availabilityBadge.classes()).toContain('availability-badge'); expect(availabilityBadge.classes()).toContain('orange'); } ); }); test('button body copy when buttonBodyCopy provided', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData, buttonBodyCopy: 'body copy' }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const buttonBodyCopy = wrapper.find(buttonBodyCopyReference); // Assert expect(buttonBodyCopy.exists()).toBeTruthy(); expect(buttonBodyCopy.classes()).toContain('m-0'); expect(buttonBodyCopy.classes()).toContain('button-label-sub-copy'); expect(buttonBodyCopy.classes()).toContain('small'); }); test('screen reader only text when screenReaderOnlyText provided', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const screenReaderOnlyText = 'text just for screen reader'; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData, screenReaderOnlyText }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const screenReaderOnlySpan = wrapper.find(screenReaderOnlySpanReference); // Assert expect(screenReaderOnlySpan.exists()).toBeTruthy(); expect(screenReaderOnlySpan.classes()).toContain('sr-only'); expect(screenReaderOnlySpan.text()).toContain(screenReaderOnlyText); }); }); describe('should not render', () => { test('button body copy when buttonBodyCopy not provided', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const buttonBodyCopy = wrapper.find(buttonBodyCopyReference); // Assert expect(buttonBodyCopy.exists()).toBeFalsy(); }); test('screen reader only text when screenReaderOnlyText not provided', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const screenReaderOnlySpan = wrapper.find(screenReaderOnlySpanReference); // Assert expect(screenReaderOnlySpan.exists()).toBeFalsy(); }); }); describe('computed', () => { describe('isLoaderDisplayed', () => { test('should return false when availabilityRating is empty string', async () => { // Arrange const availabilityRating = ''; const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const result = wrapper.vm.isLoaderDisplayed; // Assert expect(result).toBeFalsy(); }); test('should return false when availabilityRating is non-empty string', async () => { // Arrange const availabilityRating = 'not empty string'; const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const result = wrapper.vm.isLoaderDisplayed; // Assert expect(result).toBeFalsy(); }); }); describe('availabilityRatingClass', () => { test.each([null, undefined])( 'returns "gray" if availability rating null or undefined', async (availabilityRating) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const expected = 'gray'; // Act const result = wrapper.vm.availabilityRatingClass; // Assert expect(result).toBe(expected); } ); test('returns green if availability rating is "high"', async () => { // Arrange const availabilityRating = 'high'; const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const expected = 'green'; // Act const result = wrapper.vm.availabilityRatingClass; // Assert expect(result).toBe(expected); }); test.each(['', 'not high'])( 'returns orange if availability not "high"', async (availabilityRating) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const expected = 'orange'; // Act const result = wrapper.vm.availabilityRatingClass; // Assert expect(result).toBe(expected); } ); }); describe('badgeText', () => { test.each([null, undefined])( 'returns empty string if availability rating null or undefined', async (availabilityRating) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const expected = ''; // Act const result = wrapper.vm.badgeText; // Assert expect(result).toBe(expected); } ); test('returns "Appts available" if availability rating "high"', async () => { // Arrange const availabilityRating = 'high'; const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const expected = 'Appts available'; // Act const result = wrapper.vm.badgeText; // Assert expect(result).toBe(expected); }); test.each(['', 'not high'])( 'returns "Appts low" if availability rating not null or "high"', async (availabilityRating) => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve(availabilityRating) }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const expected = 'Appts low'; // Act const result = wrapper.vm.badgeText; // Assert expect(result).toBe(expected); } ); }); describe('selectedValue', () => { test('"get" returns modalValue prop', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const modelValue = 'value of modal'; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue, groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Act const result = wrapper.vm.selectedValue; // Assert expect(result).toBe(modelValue); }); test('"set" emits "update:modalValue" event', async () => { // Arrange const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback: () => Promise.resolve({}) }; const modelValue = 'value of modal'; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue, groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); const newValue = 'new value'; // Act wrapper.vm.selectedValue = newValue; // Assert expect(wrapper.emitted()['update:modelValue'][0][0]).toEqual(newValue); }); }); }); describe('before mount', () => { test('should call availabilityRatingCallback with expected data if displayAvailabilityIndicators true', async () => { // Arrange const availabilityRating = 'rating'; const availabilityRatingCallback = jest.fn().mockImplementation(() => Promise.resolve(availabilityRating)); const additionalButtonData = { displayAvailabilityIndicators: true, availabilityRatingCallback }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Assert expect(availabilityRatingCallback).toBeCalledTimes(1); }); test('should not call availabilityRatingCallback if displayAvailabilityIndicators false', async () => { // Arrange const availabilityRating = 'rating'; const availabilityRatingCallback = jest.fn().mockImplementation(() => Promise.resolve(availabilityRating)); const additionalButtonData = { displayAvailabilityIndicators: false, availabilityRatingCallback }; const wrapper = mount(shopListButton, { propsData: { value: 1234, modelValue: 'value of modal', groupName: 'name of group', buttonLabel: 'label of button', additionalButtonData }, mixins: [inputButtonWrapperMixin] }); await wrapper.vm.$nextTick(); // Assert expect(availabilityRatingCallback).toBeCalledTimes(0); }); }); });