Bladeren bron

Select: add test (#636)

杨奕 8 jaren geleden
bovenliggende
commit
2f0ba607f4

+ 1 - 1
examples/docs/zh-cn/select.md

@@ -262,7 +262,7 @@
 
 包含清空按钮,可将选择器清空为初始状态
 
-:::demo 为`el-select`设置`clearable`属性,则可将选择器清空。需要注意的是,`clearable`属性仅使用于单选。
+:::demo 为`el-select`设置`clearable`属性,则可将选择器清空。需要注意的是,`clearable`属性仅用于单选。
 ```html
 <template>
   <el-select v-model="value4" clearable>

+ 7 - 1
packages/select/src/option-group.vue

@@ -25,9 +25,15 @@
       }
     },
 
+    watch: {
+      disabled(val) {
+        this.broadcast('option', 'handleGroupDisabled', val);
+      }
+    },
+
     mounted() {
       if (this.disabled) {
-        this.broadcast('option', 'disableOptions');
+        this.broadcast('option', 'handleGroupDisabled', this.disabled);
       }
     }
   };

+ 12 - 9
packages/select/src/option.vue

@@ -4,7 +4,7 @@
     @click.stop="selectOptionClick"
     class="el-select-dropdown__item"
     v-show="queryPassed"
-    :class="{ 'selected': itemSelected, 'is-disabled': disabled, 'hover': parent.hoverIndex === index }">
+    :class="{ 'selected': itemSelected, 'is-disabled': disabled || groupDisabled, 'hover': parent.hoverIndex === index }">
     <slot>
       <span>{{ currentLabel }}</span>
     </slot>
@@ -39,13 +39,17 @@
     data() {
       return {
         index: -1,
+        groupDisabled: false,
         queryPassed: true,
-        hitState: false,
-        currentLabel: this.label
+        hitState: false
       };
     },
 
     computed: {
+      currentLabel() {
+        return this.label || ((typeof this.value === 'string' || typeof this.value === 'number') ? this.value : '');
+      },
+
       parent() {
         let result = this.$parent;
         while (!result.isSelect) {
@@ -76,18 +80,18 @@
     },
 
     methods: {
-      disableOptions() {
-        this.disabled = true;
+      handleGroupDisabled(val) {
+        this.groupDisabled = val;
       },
 
       hoverItem() {
-        if (!this.disabled) {
+        if (!this.disabled && !this.groupDisabled) {
           this.parent.hoverIndex = this.parent.options.indexOf(this);
         }
       },
 
       selectOptionClick() {
-        if (this.disabled !== true) {
+        if (this.disabled !== true && this.groupDisabled !== true) {
           this.dispatch('select', 'handleOptionClick', this);
         }
       },
@@ -107,7 +111,6 @@
     },
 
     created() {
-      this.currentLabel = this.currentLabel || ((typeof this.value === 'string' || typeof this.value === 'number') ? this.value : '');
       this.parent.options.push(this);
       this.parent.optionsCount++;
       this.parent.filteredOptionsCount++;
@@ -118,7 +121,7 @@
       }
 
       this.$on('queryChange', this.queryChange);
-      this.$on('disableOptions', this.disableOptions);
+      this.$on('handleGroupDisabled', this.handleGroupDisabled);
       this.$on('resetIndex', this.resetIndex);
     },
 

+ 9 - 5
packages/select/src/select.vue

@@ -53,11 +53,11 @@
     <transition name="md-fade-bottom" @after-leave="doDestroy">
       <el-select-menu
         ref="popper"
-        v-show="visible && nodataText !== false">
+        v-show="visible && emptyText !== false">
         <ul class="el-select-dropdown__list" v-show="options.length > 0 && filteredOptionsCount > 0 && !loading">
           <slot></slot>
         </ul>
-        <p class="el-select-dropdown__nodata" v-if="nodataText">{{ nodataText }}</p>
+        <p class="el-select-dropdown__nodata" v-if="emptyText">{{ emptyText }}</p>
       </el-select-menu>
     </transition>
   </div>
@@ -106,7 +106,7 @@
         return criteria;
       },
 
-      nodataText() {
+      emptyText() {
         if (this.loading) {
           return '加载中';
         } else {
@@ -439,7 +439,9 @@
               this.hoverIndex = 0;
             }
             this.resetScrollTop();
-            if (this.options[this.hoverIndex].disabled === true || !this.options[this.hoverIndex].queryPassed) {
+            if (this.options[this.hoverIndex].disabled === true ||
+              this.options[this.hoverIndex].groupDisabled === true ||
+              !this.options[this.hoverIndex].queryPassed) {
               this.navigateOptions('next');
             }
           }
@@ -449,7 +451,9 @@
               this.hoverIndex = this.options.length - 1;
             }
             this.resetScrollTop();
-            if (this.options[this.hoverIndex].disabled === true || !this.options[this.hoverIndex].queryPassed) {
+            if (this.options[this.hoverIndex].disabled === true ||
+              this.options[this.hoverIndex].groupDisabled === true ||
+              !this.options[this.hoverIndex].queryPassed) {
               this.navigateOptions('prev');
             }
           }

+ 410 - 0
test/unit/specs/select.spec.js

@@ -0,0 +1,410 @@
+import { createTest, createVue, triggerEvent } from '../util';
+import Select from 'packages/select';
+
+describe('Select', () => {
+  const getSelectVm = (configs = {}, options) => {
+    ['multiple', 'clearable', 'filterable', 'remote'].forEach(config => {
+      configs[config] = configs[config] || false;
+    });
+    if (!options) {
+      options = [{
+        value: '选项1',
+        label: '黄金糕',
+        disabled: false
+      }, {
+        value: '选项2',
+        label: '双皮奶',
+        disabled: false
+      }, {
+        value: '选项3',
+        label: '蚵仔煎',
+        disabled: false
+      }, {
+        value: '选项4',
+        label: '龙须面',
+        disabled: false
+      }, {
+        value: '选项5',
+        label: '北京烤鸭',
+        disabled: false
+      }];
+    }
+    const vm = createVue({
+      template: `
+        <div>
+          <el-select
+            v-model="value"
+            :multiple="multiple"
+            :clearable="clearable"
+            :filterable="filterable"
+            :filterMethod="filterMethod"
+            :remote="remote"
+            :loading="loading"
+            :remoteMethod="remoteMethod">
+            <el-option
+              v-for="item in options"
+              :label="item.label"
+              :disabled="item.disabled"
+              :value="item.value">
+            </el-option>
+          </el-select>
+        </div>
+      `,
+
+      data() {
+        return {
+          options,
+          multiple: configs.multiple,
+          clearable: configs.clearable,
+          filterable: configs.filterable,
+          loading: false,
+          filterMethod: configs.filterMethod && configs.filterMethod(this),
+          remote: configs.remote,
+          remoteMethod: configs.remoteMethod && configs.remoteMethod(this),
+          value: configs.multiple ? [] : ''
+        };
+      }
+    }, true);
+    return vm;
+  };
+
+  afterEach(() => {
+    const el = document.querySelector('.el-select');
+    if (!el) return;
+    if (el.parentNode) {
+      el.parentNode.removeChild(el);
+    }
+  });
+
+  it('create', () => {
+    const vm = createTest(Select, true);
+    expect(vm.$el.className).to.equal('el-select');
+    expect(vm.$el.querySelector('.el-input__inner').placeholder).to.equal('请选择');
+    vm.toggleMenu();
+    expect(vm.visible).to.true;
+  });
+
+  it('options rendered correctly', () => {
+    const vm = getSelectVm();
+    const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
+    const result = [].every.call(options, (option, index) => {
+      let text = option.querySelector('span').textContent;
+      return text === vm.options[index].label;
+    });
+    expect(result).to.true;
+  });
+
+  it('default value', done => {
+    const vm = createVue({
+      template: `
+        <div>
+          <el-select v-model="value">
+            <el-option
+              v-for="item in options"
+              :label="item.label"
+              :value="item.value">
+            </el-option>
+          </el-select>
+        </div>
+      `,
+
+      data() {
+        return {
+          options: [{
+            value: '选项1',
+            label: '黄金糕'
+          }, {
+            value: '选项2',
+            label: '双皮奶'
+          }],
+          value: '选项2'
+        };
+      }
+    }, true);
+    setTimeout(() => {
+      expect(vm.$el.querySelector('.el-input__inner').value).to.equal('双皮奶');
+      done();
+    }, 100);
+  });
+
+  it('single select', done => {
+    sinon.stub(window.console, 'log');
+    const vm = createVue({
+      template: `
+        <div>
+          <el-select v-model="value" @change="handleChange">
+            <el-option
+              v-for="item in options"
+              :label="item.label"
+              :value="item.value">
+              <p>{{item.label}} {{item.value}}</p>
+            </el-option>
+          </el-select>
+        </div>
+      `,
+
+      data() {
+        return {
+          options: [{
+            value: '选项1',
+            label: '黄金糕'
+          }, {
+            value: '选项2',
+            label: '双皮奶'
+          }, {
+            value: '选项3',
+            label: '蚵仔煎'
+          }, {
+            value: '选项4',
+            label: '龙须面'
+          }, {
+            value: '选项5',
+            label: '北京烤鸭'
+          }],
+          value: ''
+        };
+      },
+
+      methods: {
+        handleChange() {
+          console.log('changed');
+        }
+      }
+    }, true);
+    const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
+    expect(vm.value).to.equal('');
+    triggerEvent(options[2], 'mouseenter');
+    options[2].click();
+    setTimeout(() => {
+      expect(vm.value).to.equal('选项3');
+      expect(window.console.log.callCount).to.equal(1);
+      options[4].click();
+      setTimeout(() => {
+        expect(vm.value).to.equal('选项5');
+        expect(window.console.log.callCount).to.equal(2);
+        window.console.log.restore();
+        done();
+      }, 100);
+    }, 100);
+  });
+
+  it('disabled option', done => {
+    const vm = getSelectVm();
+    vm.options[1].disabled = true;
+    setTimeout(() => {
+      const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
+      expect(options[1].classList.contains('is-disabled')).to.true;
+      options[1].click();
+      setTimeout(() => {
+        expect(vm.value).to.equal('');
+        done();
+      }, 100);
+    }, 100);
+  });
+
+  it('disabled select', () => {
+    const vm = createTest(Select, { disabled: true }, true);
+    expect(vm.$el.querySelector('.el-input').classList.contains('is-disabled')).to.true;
+  });
+
+  it('keyboard operations', done => {
+    const vm = getSelectVm();
+    const select = vm.$children[0];
+    let i = 8;
+    while (i--) {
+      select.navigateOptions('next');
+    }
+    select.navigateOptions('prev');
+    setTimeout(() => {
+      expect(select.hoverIndex).to.equal(0);
+      select.selectOption();
+      setTimeout(() => {
+        expect(select.value).to.equal('选项1');
+        done();
+      }, 100);
+    }, 100);
+  });
+
+  it('clearable', done => {
+    const vm = getSelectVm({ clearable: true });
+    const select = vm.$children[0];
+    vm.value = '选项1';
+    select.inputHovering = true;
+    setTimeout(() => {
+      const icon = vm.$el.querySelector('.el-input__icon');
+      expect(icon.classList.contains('el-icon-circle-close')).to.true;
+      icon.click();
+      expect(vm.value).to.equal('');
+      done();
+    }, 100);
+  });
+
+  it('custom el-option template', () => {
+    const vm = createVue({
+      template: `
+        <div>
+          <el-select v-model="value">
+            <el-option
+              v-for="item in options"
+              :label="item.label"
+              :value="item.value">
+              <p>{{item.label}} {{item.value}}</p>
+            </el-option>
+          </el-select>
+        </div>
+      `,
+
+      data() {
+        return {
+          options: [{
+            value: 'value',
+            label: 'label'
+          }],
+          value: ''
+        };
+      }
+    }, true);
+    expect(vm.$el.querySelector('.el-select-dropdown__item p').textContent).to.equal('label value');
+  });
+
+  it('option group', () => {
+    const vm = createVue({
+      template: `
+        <div>
+          <el-select v-model="value">
+            <el-option-group
+              v-for="group in options"
+              :disabled="group.disabled"
+              :label="group.label">
+              <el-option
+                v-for="item in group.options"
+                :label="item.label"
+                :value="item.value">
+              </el-option>
+            </el-option-group>
+          </el-select>
+        </div>
+      `,
+
+      data() {
+        return {
+          options: [{
+            label: '热门城市',
+            options: [{
+              value: 'Shanghai',
+              label: '上海'
+            }, {
+              value: 'Beijing',
+              label: '北京'
+            }]
+          }, {
+            label: '城市名',
+            disabled: true,
+            options: [{
+              value: 'Chengdu',
+              label: '成都'
+            }, {
+              value: 'Shenzhen',
+              label: '深圳'
+            }, {
+              value: 'Guangzhou',
+              label: '广州'
+            }, {
+              value: 'Dalian',
+              label: '大连'
+            }]
+          }],
+          value: ''
+        };
+      }
+    }, true);
+    const groups = vm.$el.querySelectorAll('.el-select-group__wrap');
+    const options = groups[1].querySelectorAll('.el-select-dropdown__item');
+    expect(groups.length).to.equal(2);
+    expect(options.length).to.equal(4);
+    expect(options[0].querySelector('span').textContent).to.equal('成都');
+  });
+
+  it('filterable', done => {
+    const vm = getSelectVm({ filterable: true });
+    const select = vm.$children[0];
+    select.selectedLabel = '面';
+    select.onInputChange();
+    select.visible = true;
+    setTimeout(() => {
+      expect(select.filteredOptionsCount).to.equal(1);
+      done();
+    }, 100);
+  });
+
+  it('filterable with custom filter-method', done => {
+    const filterMethod = vm => {
+      return query => {
+        vm.options = vm.options.filter(option => option.label.indexOf(query) === -1);
+      };
+    };
+    const vm = getSelectVm({ filterable: true, filterMethod });
+    const select = vm.$children[0];
+    select.query = '面';
+    setTimeout(() => {
+      expect(select.filteredOptionsCount).to.equal(4);
+      done();
+    }, 100);
+  });
+
+  it('multiple select', done => {
+    const vm = getSelectVm({ multiple: true });
+    const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
+    vm.value = ['选项1'];
+    setTimeout(() => {
+      options[1].click();
+      options[3].click();
+      setTimeout(() => {
+        expect(vm.value.indexOf('选项2') > -1 && vm.value.indexOf('选项4') > -1).to.true;
+        const tagCloseIcons = vm.$el.querySelectorAll('.el-tag__close');
+        tagCloseIcons[0].click();
+        setTimeout(() => {
+          expect(vm.value.indexOf('选项1')).to.equal(-1);
+          done();
+        }, 100);
+      }, 100);
+    }, 100);
+  });
+
+  it('multiple remote search', done => {
+    const remoteMethod = vm => {
+      return query => {
+        vm.loading = true;
+        setTimeout(() => {
+          vm.options = vm.options.filter(option => {
+            return option.label.indexOf(query) > -1;
+          });
+          vm.loading = false;
+        }, 200);
+      };
+    };
+    const vm = getSelectVm({
+      multiple: true,
+      remote: true,
+      filterable: true,
+      remoteMethod
+    });
+    const select = vm.$children[0];
+    select.query = '面';
+    setTimeout(() => {
+      expect(select.filteredOptionsCount).to.equal(1);
+      select.query = '';
+      select.options[0].$el.click();
+      vm.$nextTick(() => {
+        expect(vm.value[0]).to.equal('选项4');
+        select.deletePrevTag({ target: select.$refs.input });
+        select.deletePrevTag({ target: select.$refs.input });
+        select.resetInputState({ keyCode: 1 });
+        vm.$nextTick(() => {
+          expect(vm.value.length).to.equal(0);
+          done();
+        });
+      });
+    }, 250);
+  });
+});