2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-19 09:50:33 +03:00

Merge branch 'master' into customizable-text

This commit is contained in:
Jeff
2020-12-22 12:34:42 -08:00
77 changed files with 5215 additions and 2863 deletions
+35
View File
@@ -0,0 +1,35 @@
import { mountDefault } from "../helpers";
describe("Search Slot Scope", () => {
/**
* @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant
*/
describe("aria-activedescendant", () => {
it("adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value", async () => {
const Select = mountDefault();
expect(
Select.vm.scope.search.attributes["aria-activedescendant"]
).toEqual(undefined);
Select.vm.open = true;
await Select.vm.$nextTick();
expect(
Select.vm.scope.search.attributes["aria-activedescendant"]
).toEqual(undefined);
});
it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => {
const Select = mountDefault();
Select.vm.open = true;
Select.vm.typeAheadPointer = 1;
await Select.vm.$nextTick();
expect(
Select.vm.scope.search.attributes["aria-activedescendant"]
).toEqual(`vs${Select.vm.uid}__option-1`);
});
});
});
+61
View File
@@ -0,0 +1,61 @@
import { mountDefault } from "../helpers";
describe("Automatic Scrolling", () => {
it("should check if the scroll position needs to be adjusted on up arrow keyUp", async () => {
// Given
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.find({ ref: "search" }).trigger("keydown.up");
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalled();
});
it("should check if the scroll position needs to be adjusted on down arrow keyUp", async () => {
// Given
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.find({ ref: "search" }).trigger("keydown.down");
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalled();
});
it("should check if the scroll position needs to be adjusted when filtered options changes", async () => {
// Given
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.vm.search = "two";
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalled();
});
it("should not adjust scroll position when autoscroll is false", async () => {
// Given
const Select = mountDefault({
autoscroll: false
});
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
// When
Select.vm.search = "two";
await Select.vm.$nextTick();
// Then
expect(spy).toHaveBeenCalledTimes(0);
});
});
+30
View File
@@ -0,0 +1,30 @@
import { searchSubmit, selectTag, selectWithProps } from "../helpers";
describe("CreateOption When Tagging Is Enabled", () => {
it("can select the current search text as a string", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: ["one", "two"],
createOption: option => "four"
});
await selectTag(Select, "three");
expect(Select.vm.selectedValue).toEqual(["four"]);
});
it("can select the current search text as an object", async () => {
const Select = selectWithProps({
taggable: true,
multiple: false,
value: null,
options: [],
label: "name",
createOption: title => ({ name: title })
});
await selectTag(Select, "two");
expect(Select.emitted("input")[0]).toEqual([{ name: "two" }]);
});
});
+12 -1
View File
@@ -1,4 +1,4 @@
import { selectWithProps } from "../helpers";
import { mountDefault, selectWithProps } from '../helpers';
describe("Removing values", () => {
it("can remove the given tag when its close icon is clicked", async () => {
@@ -48,6 +48,17 @@ describe("Removing values", () => {
expect(Select.vm.selectedValue).toEqual([]);
});
it('will not emit input event if value has not changed with backspace', () => {
const Select = mountDefault();
Select.vm.$data._value = 'one';
Select.find({ ref: 'search' }).trigger('keydown.backspace');
expect(Select.emitted().input.length).toBe(1);
Select.find({ ref: 'search' }).trigger('keydown.backspace');
Select.find({ ref: 'search' }).trigger('keydown.backspace');
expect(Select.emitted().input.length).toBe(1);
});
describe("Clear button", () => {
it("should be displayed on single select when value is selected", () => {
const Select = selectWithProps({
+27 -6
View File
@@ -1,10 +1,16 @@
import { selectWithProps } from "../helpers";
import OpenIndicator from "../../src/components/OpenIndicator";
const preventDefault = jest.fn()
function clickEvent (currentTarget) {
return { currentTarget, preventDefault }
}
describe("Toggling Dropdown", () => {
it("should not open the dropdown when the el is clicked but the component is disabled", () => {
const Select = selectWithProps({ disabled: true });
Select.vm.toggleDropdown({ target: Select.vm.$refs.search });
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(false);
});
@@ -14,10 +20,23 @@ describe("Toggling Dropdown", () => {
options: [{ label: "one" }]
});
Select.vm.toggleDropdown({ target: Select.vm.$refs.search });
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
});
it("should not close the dropdown when the el is clicked and enableMouseInputSearch is set to true", () => {
const Select = selectWithProps({
value: [{ label: "one" }],
options: [{ label: "one" }],
enableMouseSearchInput: true
});
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
Select.vm.toggleDropdown(clickEvent(Select.vm.$el));
expect(Select.vm.open).toEqual(false)
});
it("should open the dropdown when the selected tag is clicked", () => {
const Select = selectWithProps({
value: [{ label: "one" }],
@@ -26,7 +45,7 @@ describe("Toggling Dropdown", () => {
const selectedTag = Select.find(".vs__selected").element;
Select.vm.toggleDropdown({ target: selectedTag });
Select.vm.toggleDropdown(clickEvent(selectedTag));
expect(Select.vm.open).toEqual(true);
});
@@ -35,7 +54,7 @@ describe("Toggling Dropdown", () => {
const spy = jest.spyOn(Select.vm.$refs.search, "blur");
Select.vm.open = true;
Select.vm.toggleDropdown({ target: Select.vm.$el });
Select.vm.toggleDropdown(clickEvent(Select.vm.$el));
expect(spy).toHaveBeenCalled();
});
@@ -133,11 +152,13 @@ describe("Toggling Dropdown", () => {
const Select = selectWithProps({
noDrop: true,
});
Select.vm.toggleDropdown({ target: Select.vm.$refs.search });
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
await Select.vm.$nextTick();
expect(Select.find('.vs__dropdown-menu').element.style['display']).toEqual('none');
expect(Select.contains('.vs__dropdown-menu')).toBeFalsy();
expect(Select.contains('.vs__dropdown-option')).toBeFalsy();
expect(Select.contains('.vs__no-options')).toBeFalsy();
expect(Select.vm.stateClasses['vs--open']).toBeFalsy();
+15 -15
View File
@@ -5,10 +5,10 @@ describe('Custom Keydown Handlers', () => {
it('can use the map-keydown prop to trigger custom behaviour', () => {
const onKeyDown = jest.fn();
const Select = mountDefault({
mapKeydown: (defaults, vm) => ({...defaults, 32: onKeyDown}),
mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }),
});
Select.find({ref: 'search'}).trigger('keydown.space');
Select.find({ ref: 'search' }).trigger('keydown.space');
expect(onKeyDown.mock.calls.length).toBe(1);
});
@@ -20,7 +20,7 @@ describe('Custom Keydown Handlers', () => {
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('keydown.space');
Select.find({ ref: 'search' }).trigger('keydown.space');
expect(spy).toHaveBeenCalledTimes(1);
});
@@ -28,16 +28,16 @@ describe('Custom Keydown Handlers', () => {
it('even works when combining selectOnKeyCodes with map-keydown', () => {
const onKeyDown = jest.fn();
const Select = mountDefault({
mapKeydown: (defaults, vm) => ({...defaults, 32: onKeyDown}),
mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }),
selectOnKeyCodes: [9],
});
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('keydown.space');
Select.find({ ref: 'search' }).trigger('keydown.space');
expect(onKeyDown.mock.calls.length).toBe(1);
Select.find({ref: 'search'}).trigger('keydown.tab');
Select.find({ ref: 'search' }).trigger('keydown.tab');
expect(spy).toHaveBeenCalledTimes(1);
});
@@ -47,25 +47,25 @@ describe('Custom Keydown Handlers', () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('compositionstart');
Select.find({ref: 'search'}).trigger('keydown.enter');
Select.find({ ref: 'search' }).trigger('compositionstart');
Select.find({ ref: 'search' }).trigger('keydown.enter');
expect(spy).toHaveBeenCalledTimes(0);
Select.find({ref: 'search'}).trigger('compositionend');
Select.find({ref: 'search'}).trigger('keydown.enter');
Select.find({ ref: 'search' }).trigger('compositionend');
Select.find({ ref: 'search' }).trigger('keydown.enter');
expect(spy).toHaveBeenCalledTimes(1);
});
it('will not select a value with tab if the user is composing', () => {
const Select = mountDefault({selectOnTab: true});
const Select = mountDefault({ selectOnTab: true });
const spy = jest.spyOn(Select.vm, 'typeAheadSelect');
Select.find({ref: 'search'}).trigger('compositionstart');
Select.find({ref: 'search'}).trigger('keydown.tab');
Select.find({ ref: 'search' }).trigger('compositionstart');
Select.find({ ref: 'search' }).trigger('keydown.tab');
expect(spy).toHaveBeenCalledTimes(0);
Select.find({ref: 'search'}).trigger('compositionend');
Select.find({ref: 'search'}).trigger('keydown.tab');
Select.find({ ref: 'search' }).trigger('compositionend');
Select.find({ ref: 'search' }).trigger('keydown.tab');
expect(spy).toHaveBeenCalledTimes(1);
});
+37
View File
@@ -41,4 +41,41 @@ describe("Labels", () => {
Select.vm.$data._value = "one";
expect(Select.vm.searchPlaceholder).not.toBeDefined();
});
describe('getOptionLabel', () => {
it('will return undefined if the option lacks the label key', () => {
const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({ label: 'label' });
expect(getOptionLabel({name: 'vue'})).toEqual(undefined);
});
it('will return a string value for a valid key', () => {
const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({ label: 'label' });
expect(getOptionLabel({label: 'vue'})).toEqual('vue');
});
/**
* this test fails because of a bug where Vue executes the default contents
* of a slot, even if it is implemented by the consumer.
* @see https://github.com/vuejs/vue/issues/10224
* @see https://github.com/vuejs/vue/pull/10229
*/
xit('will not call getOptionLabel if both scoped option slots are used and a filter is provided', () => {
const spy = spyOn(VueSelect.props.getOptionLabel, 'default');
const Select = shallowMount(VueSelect, {
propsData: {
options: [{name: 'one'}],
filter: () => {},
},
scopedSlots: {
'option': '<span class="option">{{ props.name }}</span>',
'selected-option': '<span class="selected">{{ props.name }}</span>',
},
});
Select.vm.select({name: 'one'});
expect(spy).toHaveBeenCalledTimes(0);
expect(Select.find('.selected').exists()).toBeTruthy();
});
});
});
+31
View File
@@ -0,0 +1,31 @@
import Select from '../../src/components/Select';
describe('Comparing Options', () => {
const comparator = Select.methods.optionComparator.bind({
getOptionKey: Select.props.getOptionKey.default,
});
it('can compare numbers', () => {
expect(comparator(1, 2)).toBeFalsy();
expect(comparator(1, 1)).toBeTruthy();
});
it('can compare strings', () => {
expect(comparator('one', 'one')).toBeTruthy();
expect(comparator('one', 'two')).toBeFalsy();
});
it('can compare objects', () => {
// compare ID keys
expect(comparator({label: 'halo', id: 1}, {label: 'halo', id: 2}))
.toBeFalsy();
// compare objects
expect(comparator({label: 'halo', value: 1}, {label: 'halo', value: 1}))
.toBeTruthy();
// compare objects with different orders
expect(comparator({value: 1, label: 'halo'}, {label: 'halo', value: 1}))
.toBeTruthy();
});
});
+2 -2
View File
@@ -5,11 +5,11 @@ describe('Serializing Option Keys', () => {
const getOptionKey = Select.props.getOptionKey.default;
it('can serialize strings to a key', () => {
expect(getOptionKey('vue')).toBe('"vue"');
expect(getOptionKey('vue')).toBe('vue');
});
it('can serialize integers to a key', () => {
expect(getOptionKey(1)).toBe('1');
expect(getOptionKey(1)).toBe(1);
});
it('can serialize objects to a key', () => {
+63 -4
View File
@@ -100,19 +100,36 @@ describe("When reduce prop is defined", () => {
expect(Select.vm.selectedValue).toEqual([]);
});
it("can use v-model syntax for a two way binding to a parent component", () => {
it("can use v-model syntax for a two way binding to a parent component", async () => {
const Parent = mount({
data: () => ({
reduce: option => option.value,
value: "foo",
current: "foo",
options: [
{ label: "This is Foo", value: "foo" },
{ label: "This is Bar", value: "bar" },
{ label: "This is Baz", value: "baz" }
]
}),
template: `<div><v-select :reduce="option => option.value" :options="options" v-model="value"></v-select></div>`,
components: { "v-select": VueSelect }
components: { "v-select": VueSelect },
computed: {
value: {
get() {
return this.current;
},
set(value) {
if (value == 'baz') return;
this.current = value;
}
}
},
template: `
<v-select
v-model="value"
:reduce="option => option.value"
:options="options"
/>
`
});
const Select = Parent.vm.$children[0];
@@ -120,7 +137,15 @@ describe("When reduce prop is defined", () => {
expect(Select.selectedValue).toEqual([{ label: "This is Foo", value: "foo" }]);
Select.select({ label: "This is Bar", value: "bar" });
await Select.$nextTick();
expect(Parent.vm.value).toEqual("bar");
expect(Select.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]);
// Parent denies to set baz
Select.select({ label: "This is Baz", value: "baz" });
await Select.$nextTick();
expect(Select.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]);
expect(Parent.vm.value).toEqual('bar');
});
it("can generate labels using a custom label key", () => {
@@ -226,4 +251,38 @@ describe("When reduce prop is defined", () => {
expect(Select.vm.selectedValue).toEqual([optionToChangeTo]);
});
describe('Reducing Tags', () => {
it('tracks values that have been created by the user', async () => {
const Parent = mount({
data: () => ({selected: null, options: []}),
template: `
<v-select
v-model="selected"
:options="options"
taggable
:reduce="name => name.value"
:create-option="label => ({ label, value: -1 })"
/>
`,
components: {'v-select': VueSelect},
});
const Select = Parent.vm.$children[0];
// When
Select.$refs.search.focus();
await Select.$nextTick();
Select.search = 'hello';
await Select.$nextTick();
Select.typeAheadSelect();
await Select.$nextTick();
// Then
expect(Select.selectedValue).toEqual([{label: 'hello', value: -1}]);
expect(Select.$refs.selectedOptions.textContent.trim()).toEqual('hello');
expect(Parent.vm.selected).toEqual(-1);
});
});
});
+92 -2
View File
@@ -1,5 +1,6 @@
import { mount, shallowMount } from "@vue/test-utils";
import VueSelect from "../../src/components/Select.vue";
import { mountDefault } from '../helpers';
describe("VS - Selecting Values", () => {
let defaultProps;
@@ -192,10 +193,20 @@ describe("VS - Selecting Values", () => {
value: [{ label: "foo", value: "bar" }]
}
});
expect(Select.vm.isOptionSelected("foo")).toEqual(true);
expect(Select.vm.isOptionSelected({ label: "foo", value: "bar" })).toEqual(true);
});
describe("change Event", () => {
it('can select two options with the same label', () => {
const options = [{label: 'one', id: 1}, {label: 'one', id: 2}];
const Select = mountDefault({options, multiple: true});
Select.vm.select({label: 'one', id: 1});
Select.vm.select({label: 'one', id: 2});
expect(Select.vm.selectedValue).toEqual(options);
});
describe("input Event", () => {
it("will trigger the input event when the selection changes", () => {
const Select = shallowMount(VueSelect);
Select.vm.select("bar");
@@ -209,5 +220,84 @@ describe("VS - Selecting Values", () => {
Select.vm.select("bar");
expect(Select.emitted("input")[0]).toEqual([["foo", "bar"]]);
});
it("will not trigger the input event when multiple is true and selection is repeated", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo ", "bar"], options: ["foo", "bar", "baz"] }
});
Select.vm.select("bar");
expect(Select.emitted("input")).toBeFalsy();
});
});
describe("option:selecting Event", () => {
it("will trigger the option:selecting event when an option is selected", () => {
const Select = shallowMount(VueSelect);
Select.vm.select("bar");
expect(Select.emitted("option:selecting")[0]).toEqual(["bar"]);
});
it("will trigger the option:selecting event regardless of current value", () => {
const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.select("foo");
Select.vm.select("bar");
expect(Select.emitted("option:selecting")).toEqual([["foo"], ["bar"]]);
});
it("will trigger the option:selecting event with current selected item when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.select("bar");
expect(Select.emitted("option:selecting")[0]).toEqual(["bar"]);
});
it("will trigger the option:selecting event regardless of current value when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo", "bar"], options: ["foo", "bar"] }
});
Select.vm.select("bar");
Select.vm.select("bar");
expect(Select.emitted("option:selecting")).toEqual([["bar"], ["bar"]]);
});
});
describe("option:deselected Event", () => {
it("will trigger the option:deselected event when an option is deselected", () => {
const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.deselect("foo");
expect(Select.emitted("option:deselected")[0]).toEqual(["foo"]);
});
it("will trigger the option:deselected event regardless of current value", () => {
const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.deselect("foo");
Select.vm.deselect("bar");
expect(Select.emitted("option:deselected")).toEqual([["foo"], ["bar"]]);
});
it("will trigger the option:selected event with current selected item when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] }
});
Select.vm.deselect("bar");
expect(Select.emitted("option:deselected")[0]).toEqual(["bar"]);
});
it("will trigger the option:selected event regardless of current value when multiple is true", () => {
const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo", "bar"], options: ["foo", "bar"] }
});
Select.vm.deselect("bar");
Select.vm.deselect("bar");
expect(Select.emitted("option:deselected")).toEqual([["bar"], ["bar"]]);
});
});
});
+47
View File
@@ -68,8 +68,55 @@ describe('Scoped Slots', () => {
await Select.vm.$nextTick();
expect(noOptions).toHaveBeenCalledWith({
loading: false,
search: 'something not there',
searching: true,
})
});
test('header slot props', async () => {
const header = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {header: header},
});
await Select.vm.$nextTick();
expect(Object.keys(header.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'deselect',
]);
});
test('footer slot props', async () => {
const footer = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {footer: footer},
});
await Select.vm.$nextTick();
expect(Object.keys(footer.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'deselect',
]);
});
test('list-header slot props', async () => {
const header = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {'list-header': header},
});
Select.vm.open = true;
await Select.vm.$nextTick();
expect(Object.keys(header.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions',
]);
});
test('list-footer slot props', async () => {
const footer = jest.fn();
const Select = mountDefault({}, {
scopedSlots: {'list-footer': footer},
});
Select.vm.open = true;
await Select.vm.$nextTick();
expect(Object.keys(footer.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions',
]);
});
});
+52 -29
View File
@@ -1,6 +1,13 @@
import { searchSubmit, selectWithProps } from "../helpers";
import {
mountDefault,
searchSubmit,
selectTag,
selectWithProps,
} from '../helpers';
import Select from '../../src/components/Select';
describe("When Tagging Is Enabled", () => {
it("can determine if a given option string already exists", () => {
const Select = selectWithProps({ taggable: true, options: ["one", "two"] });
expect(Select.vm.optionExists("one")).toEqual(true);
@@ -13,8 +20,8 @@ describe("When Tagging Is Enabled", () => {
options: [{ label: "one" }, { label: "two" }]
});
expect(Select.vm.optionExists("one")).toEqual(true);
expect(Select.vm.optionExists("three")).toEqual(false);
expect(Select.vm.optionExists({label: "one"})).toEqual(true);
expect(Select.vm.optionExists({label: "three"})).toEqual(false);
});
it("can determine if a given option object already exists when using custom labels", () => {
@@ -24,11 +31,13 @@ describe("When Tagging Is Enabled", () => {
label: "foo"
});
expect(Select.vm.optionExists("one")).toEqual(true);
expect(Select.vm.optionExists("three")).toEqual(false);
const createOption = (text) => Select.vm.createOption(text);
expect(Select.vm.optionExists(createOption("one"))).toEqual(true);
expect(Select.vm.optionExists(createOption("three"))).toEqual(false);
});
it("can add the current search text as the first item in the options list", () => {
it("can add the current search text as the first item in the options list", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
@@ -37,36 +46,37 @@ describe("When Tagging Is Enabled", () => {
});
Select.vm.search = "three";
await Select.vm.$nextTick();
expect(Select.vm.filteredOptions).toEqual(["three"]);
});
it("can select the current search text as a string", () => {
it("can select the current search text as a string", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.selectedValue).toEqual(["three"]);
});
it("can select the current search text as an object", () => {
it("can select the current search text as an object", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: [{ label: "one" }]
});
searchSubmit(Select, "two");
await selectTag(Select, "two");
expect(Select.vm.selectedValue).toEqual([
{ label: "two" }
]);
});
it("should add a freshly created option/tag to the options list when pushTags is true", () => {
it("should add a freshly created option/tag to the options list when pushTags is true", async () => {
const Select = selectWithProps({
pushTags: true,
taggable: true,
@@ -75,12 +85,12 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.pushedTags).toEqual(["three"]);
expect(Select.vm.optionList).toEqual(["one", "two", "three"]);
});
it("should pushTags even if the consumer has defined a createOption callback", () => {
it("should pushTags even if the consumer has defined a createOption callback", async () => {
const Select = selectWithProps({
pushTags: true,
taggable: true,
@@ -88,13 +98,13 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.pushedTags).toEqual(["three"]);
expect(Select.vm.optionList).toEqual(["one", "two", "three"]);
});
it("should add a freshly created option/tag to the options list when pushTags is true and filterable is false", () => {
it("should add a freshly created option/tag to the options list when pushTags is true and filterable is false", async () => {
const Select = selectWithProps({
filterable: false,
pushTags: true,
@@ -104,13 +114,13 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.pushedTags).toEqual(["three"]);
expect(Select.vm.optionList).toEqual(["one", "two", "three"]);
expect(Select.vm.filteredOptions).toEqual(["one", "two", "three"]);
});
it("wont add a freshly created option/tag to the options list when pushTags is false", () => {
it("wont add a freshly created option/tag to the options list when pushTags is false", async () => {
const Select = selectWithProps({
pushTags: false,
taggable: true,
@@ -119,11 +129,11 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.optionList).toEqual(["one", "two"]);
});
it("wont add a freshly created option/tag to the options list when pushTags is false and filterable is false", () => {
it("wont add a freshly created option/tag to the options list when pushTags is false and filterable is false", async () => {
const Select = selectWithProps({
filterable: false,
pushTags: false,
@@ -133,7 +143,7 @@ describe("When Tagging Is Enabled", () => {
options: ["one", "two"]
});
searchSubmit(Select, "three");
await selectTag(Select, "three");
expect(Select.vm.optionList).toEqual(["one", "two"]);
expect(Select.vm.filteredOptions).toEqual(["one", "two"]);
});
@@ -146,9 +156,7 @@ describe("When Tagging Is Enabled", () => {
options: ["one", two]
});
Select.vm.search = "two";
searchSubmit(Select);
await selectTag(Select, "two");
expect(Select.vm.selectedValue).toEqual([two]);
});
@@ -207,34 +215,49 @@ describe("When Tagging Is Enabled", () => {
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]);
});
it("should not allow duplicate tags when using string options", () => {
it("should not allow duplicate tags when using string options", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true
});
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual(["one"]);
expect(Select.vm.search).toEqual("");
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual(["one"]);
expect(Select.vm.search).toEqual("");
});
it("should not allow duplicate tags when using object options", () => {
it("should not allow duplicate tags when using object options", async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: [{ label: "two" }]
});
const spy = jest.spyOn(Select.vm, 'select');
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]);
expect(spy).lastCalledWith({label: 'one'});
expect(Select.vm.search).toEqual("");
searchSubmit(Select, "one");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]);
expect(Select.vm.search).toEqual("");
});
it("will select an existing option on tab", async () => {
const Select = mountDefault({
taggable: true,
selectOnTab: true
});
Select.vm.typeAheadPointer = 0;
Select.find({ ref: "search" }).trigger("keydown.tab");
await Select.vm.$nextTick();
expect(Select.vm.selectedValue).toEqual(['one']);
})
});
+7 -118
View File
@@ -1,18 +1,17 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMount } from "@vue/test-utils";
import VueSelect from "../../src/components/Select";
import { mountDefault, mountWithoutTestUtils } from '../helpers';
import typeAheadMixin from '../../src/mixins/typeAheadPointer';
import Vue from 'vue';
import { mountDefault, mountWithoutTestUtils } from "../helpers";
import typeAheadMixin from "../../src/mixins/typeAheadPointer";
import Vue from "vue";
describe("Moving the Typeahead Pointer", () => {
it('should set the pointer to zero when the filteredOptions watcher is called', async () => {
it("should set the pointer to zero when the filteredOptions watcher is called", async () => {
const Select = shallowMount(VueSelect, {
propsData: { options: ['one', 'two', 'three'] },
propsData: { options: ["one", "two", "three"] },
sync: false
});
Select.vm.search = 'one';
Select.vm.search = "one";
await Select.vm.$nextTick();
expect(Select.vm.typeAheadPointer).toEqual(0);
@@ -45,114 +44,4 @@ describe("Moving the Typeahead Pointer", () => {
Select.vm.typeAheadDown();
expect(Select.vm.typeAheadPointer).toEqual(2);
});
describe("Automatic Scrolling", () => {
it("should check if the scroll position needs to be adjusted on up arrow keyUp", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
Select.find({ ref: "search" }).trigger("keydown.up");
expect(spy).toHaveBeenCalled();
});
it("should check if the scroll position needs to be adjusted on down arrow keyUp", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.typeAheadPointer = 1;
Select.find({ ref: "search" }).trigger("keydown.down");
expect(spy).toHaveBeenCalled();
});
/**
* This test fails despite working in the browser.
* After many attempts to get it to pass, it's been
* rewritten below.
*/
it.skip("should check if the scroll position needs to be adjusted when filtered options changes", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll");
Select.vm.search = "two";
expect(spy).toHaveBeenCalled();
});
it("should scroll up if the pointer is above the current viewport bounds", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "scrollTo");
Select.setMethods({
pixelsToPointerTop() {
return 1;
},
viewport() {
return { top: 2, bottom: 0 };
}
});
Select.vm.maybeAdjustScroll();
expect(spy).toHaveBeenCalledWith(1);
});
it("should scroll down if the pointer is below the current viewport bounds", () => {
const Select = mountDefault();
const spy = jest.spyOn(Select.vm, "scrollTo");
Select.setMethods({
pixelsToPointerBottom() {
return 2;
},
viewport() {
return { top: 0, bottom: 1 };
}
});
Select.vm.maybeAdjustScroll();
expect(spy).toHaveBeenCalledWith(
Select.vm.viewport().top + Select.vm.pointerHeight()
);
});
});
describe("Measuring pixel distances", () => {
it("should calculate pointerHeight as the offsetHeight of the pointer element if it exists", async () => {
const Select = mountDefault();
// Drop down must be open for $refs to exist
Select.vm.open = true;
await Select.vm.$nextTick();
/**
* Since JSDom doesn't render layouts, set the offsetHeight explicitly
* to 25px for each list item.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
*/
let i = 0;
for (let option of Select.vm.$refs.dropdownMenu.children) {
Object.defineProperty(option, "offsetHeight", {
value: 1 + i
});
i++;
}
// Fresh instances start with the pointer at -1
Select.vm.typeAheadPointer = -1;
expect(Select.vm.pointerHeight()).toEqual(0);
Select.vm.typeAheadPointer = 0;
expect(Select.vm.pointerHeight()).toEqual(1);
Select.vm.typeAheadPointer = 1;
expect(Select.vm.pointerHeight()).toEqual(2);
Select.vm.typeAheadPointer = 2;
expect(Select.vm.pointerHeight()).toEqual(3);
});
});
});
@@ -0,0 +1,14 @@
import sortAndStringify from '../../../src/utility/sortAndStringify';
test('it will stringify an object', () => {
expect(sortAndStringify({hello: 'world'})).toEqual('{"hello":"world"}');
});
test('it will sort attributes alphabetically', () => {
expect(sortAndStringify({b: 'b', a: 'a'})).toEqual('{"a":"a","b":"b"}');
});
test('comparing two objects with unsorted keys', () => {
expect(sortAndStringify({b: 'b', a: 'a'}))
.toEqual(sortAndStringify({a: 'a', b: 'b'}))
});