vue-instantsearch
Version:
👀 Lightning-fast Algolia search for Vue apps
259 lines (220 loc) • 6.15 kB
JavaScript
/**
* @jest-environment jsdom
*/
import { mount } from '../../../test/utils';
import { __setState } from '../../mixins/widget';
import MenuSelect from '../MenuSelect.vue';
import '../../../test/utils/sortedHtmlSerializer';
jest.mock('../../mixins/widget');
jest.mock('../../mixins/panel');
const defaultState = {
canRefine: true,
items: [
{ label: 'Apple', value: 'Apple', isRefined: false, count: 50 },
{ label: 'Samsung', value: 'Samsung', isRefined: false, count: 20 },
{ label: 'Sony', value: 'Sony', isRefined: false, count: 15 },
],
};
const defaultProps = {
attribute: 'brand',
};
describe('default render', () => {
it('renders correctly with custom label', () => {
__setState({
...defaultState,
});
const props = {
...defaultProps,
};
const wrapper = mount(MenuSelect, {
propsData: props,
slots: {
// tag is needed here for Vue Test Utils, even if it's invalid HTML
defaultOption: '<span>None</span>',
},
});
expect(wrapper.find('option').html()).toContain('None');
expect(wrapper.html()).toMatchSnapshot();
});
});
it('exposes send-event method for insights middleware', async () => {
const sendEvent = jest.fn();
__setState({
...defaultState,
sendEvent,
});
const wrapper = mount({
components: { MenuSelect },
data() {
return { props: defaultProps };
},
template: `
<MenuSelect v-bind="props">
<template v-slot="{ sendEvent }">
<div>
<button ="sendEvent()">Send Event</button>
</div>
</template>
</MenuSelect>
`,
});
await wrapper.find('button').trigger('click');
expect(sendEvent).toHaveBeenCalledTimes(1);
});
describe('custom item slot', () => {
it('renders correctly', () => {
__setState({
...defaultState,
});
const wrapper = mount({
components: { MenuSelect },
data() {
return { props: defaultProps };
},
template: `
<MenuSelect v-bind="props">
<template v-slot:item="{ item }">
<span>
{{ item.label.toUpperCase() }}
</span>
</template>
</MenuSelect>
`,
});
expect(wrapper.html()).toMatchSnapshot();
expect(wrapper.find('option:nth-child(2)').html()).toMatch(
/<span>\s*APPLE\s*<\/span>/
);
});
});
describe('custom default render', () => {
const defaultSlot = `
<template v-slot="{ items, canRefine, refine }">
<select
="refine($event.currentTarget.value)"
:disabled="!canRefine"
>
<option value="">All</option>
<option
v-for="item in items"
:key="item.value"
:value="item.value"
:selected="item.isRefined"
>
{{item.label.toUpperCase()}}
</option>
</select>
</template>
`;
it('renders correctly', () => {
__setState({
...defaultState,
});
const wrapper = mount({
components: { MenuSelect },
data() {
return { props: defaultProps };
},
template: `
<MenuSelect v-bind="props">
${defaultSlot}
</MenuSelect>
`,
});
expect(wrapper.html()).toMatchSnapshot();
});
it('renders correctly with a selected value', () => {
__setState({
...defaultState,
items: [
{ label: 'Apple', value: 'Apple', isRefined: false, count: 50 },
{ label: 'Samsung', value: 'Samsung', isRefined: true, count: 20 },
{ label: 'Sony', value: 'Sony', isRefined: false, count: 15 },
],
});
const wrapper = mount({
components: { MenuSelect },
data() {
return { props: defaultProps };
},
template: `
<MenuSelect v-bind="props">
<template v-slot="{ items, canRefine, refine }">
<select
="refine($event.currentTarget.value)"
:disabled="!canRefine"
>
<option value="">All</option>
<option
v-for="item in items"
:key="item.value"
:value="item.value"
:selected="item.isRefined"
:data-test="item.value"
>
{{item.label}}
</option>
</select>
</template>
</MenuSelect>
`,
});
// Unlike in vue 2, in vue 3 when value and textContent are the same,
// Vue doesn't render the value attribute at all.
// For example,
// In Vue 2,
// <option value="Apple">Apple</option>
// becomes, in Vue 3
// <option>Apple</option>
const selected = wrapper.find('[data-test="Samsung"]');
const options = wrapper.findAll('option:not([data-test="Samsung"])');
expect(selected.element.selected).toBe(true);
for (let i = 0; i < options.length; i++) {
expect((options[i] || options.at(i)).element.selected).toBe(false);
}
});
it('renders correctly without refinements', () => {
__setState({
...defaultState,
canRefine: false,
items: [],
});
const wrapper = mount({
components: { MenuSelect },
data() {
return { props: defaultProps };
},
template: `
<MenuSelect v-bind="props">
${defaultSlot}
</MenuSelect>
`,
});
expect(wrapper.htmlCompat()).toMatchSnapshot();
});
it('calls refine on select change', async () => {
const refine = jest.fn();
__setState({
...defaultState,
refine,
});
const wrapper = mount({
components: { MenuSelect },
data() {
return { props: defaultProps };
},
template: `
<MenuSelect v-bind="props">
${defaultSlot}
</MenuSelect>
`,
});
expect(refine).not.toHaveBeenCalled();
const select = wrapper.find('select');
// Simulate the change
select.element.value = 'Apple';
await select.trigger('change');
expect(refine).toHaveBeenCalledTimes(1);
expect(refine).toHaveBeenCalledWith('Apple');
});
});