select.spec.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. import { createTest, createVue, triggerEvent, destroyVM } from '../util';
  2. import Select from 'packages/select';
  3. describe('Select', () => {
  4. const getSelectVm = (configs = {}, options) => {
  5. ['multiple', 'clearable', 'filterable', 'allowCreate', 'remote'].forEach(config => {
  6. configs[config] = configs[config] || false;
  7. });
  8. configs.multipleLimit = configs.multipleLimit || 0;
  9. if (!options) {
  10. options = [{
  11. value: '选项1',
  12. label: '黄金糕',
  13. disabled: false
  14. }, {
  15. value: '选项2',
  16. label: '双皮奶',
  17. disabled: false
  18. }, {
  19. value: '选项3',
  20. label: '蚵仔煎',
  21. disabled: false
  22. }, {
  23. value: '选项4',
  24. label: '龙须面',
  25. disabled: false
  26. }, {
  27. value: '选项5',
  28. label: '北京烤鸭',
  29. disabled: false
  30. }];
  31. }
  32. const vm = createVue({
  33. template: `
  34. <div>
  35. <el-select
  36. v-model="value"
  37. :multiple="multiple"
  38. :multiple-limit="multipleLimit"
  39. :popper-class="popperClass"
  40. :clearable="clearable"
  41. :filterable="filterable"
  42. :allow-create="allowCreate"
  43. :filterMethod="filterMethod"
  44. :remote="remote"
  45. :loading="loading"
  46. :remoteMethod="remoteMethod">
  47. <el-option
  48. v-for="item in options"
  49. :label="item.label"
  50. :key="item.value"
  51. :disabled="item.disabled"
  52. :value="item.value">
  53. </el-option>
  54. </el-select>
  55. </div>
  56. `,
  57. data() {
  58. return {
  59. options,
  60. multiple: configs.multiple,
  61. multipleLimit: configs.multipleLimit,
  62. clearable: configs.clearable,
  63. filterable: configs.filterable,
  64. allowCreate: configs.allowCreate,
  65. popperClass: configs.popperClass,
  66. loading: false,
  67. filterMethod: configs.filterMethod && configs.filterMethod(this),
  68. remote: configs.remote,
  69. remoteMethod: configs.remoteMethod && configs.remoteMethod(this),
  70. value: configs.multiple ? [] : ''
  71. };
  72. }
  73. }, true);
  74. return vm;
  75. };
  76. let vm;
  77. afterEach(() => {
  78. destroyVM(vm);
  79. });
  80. it('create', () => {
  81. vm = createTest(Select, true);
  82. expect(vm.$el.className).to.equal('el-select');
  83. expect(vm.$el.querySelector('.el-input__inner').placeholder).to.equal('请选择');
  84. vm.toggleMenu();
  85. expect(vm.visible).to.true;
  86. });
  87. it('options rendered correctly', () => {
  88. vm = getSelectVm();
  89. const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
  90. const result = [].every.call(options, (option, index) => {
  91. let text = option.querySelector('span').textContent;
  92. return text === vm.options[index].label;
  93. });
  94. expect(result).to.true;
  95. });
  96. it('custom dropdown class', () => {
  97. vm = getSelectVm({ popperClass: 'custom-dropdown' });
  98. const dropdown = vm.$el.querySelector('.el-select-dropdown');
  99. expect(dropdown.classList.contains('custom-dropdown')).to.true;
  100. });
  101. it('default value', done => {
  102. vm = createVue({
  103. template: `
  104. <div>
  105. <el-select v-model="value">
  106. <el-option
  107. v-for="item in options"
  108. :label="item.label"
  109. :value="item.value">
  110. </el-option>
  111. </el-select>
  112. </div>
  113. `,
  114. data() {
  115. return {
  116. options: [{
  117. value: '选项1',
  118. label: '黄金糕'
  119. }, {
  120. value: '选项2',
  121. label: '双皮奶'
  122. }],
  123. value: '选项2'
  124. };
  125. }
  126. }, true);
  127. setTimeout(() => {
  128. expect(vm.$el.querySelector('.el-input__inner').value).to.equal('双皮奶');
  129. done();
  130. }, 100);
  131. });
  132. it('single select', done => {
  133. sinon.stub(window.console, 'log');
  134. vm = createVue({
  135. template: `
  136. <div>
  137. <el-select v-model="value" @change="handleChange">
  138. <el-option
  139. v-for="item in options"
  140. :label="item.label"
  141. :value="item.value">
  142. <p>{{item.label}} {{item.value}}</p>
  143. </el-option>
  144. </el-select>
  145. </div>
  146. `,
  147. data() {
  148. return {
  149. options: [{
  150. value: '选项1',
  151. label: '黄金糕'
  152. }, {
  153. value: '选项2',
  154. label: '双皮奶'
  155. }, {
  156. value: '选项3',
  157. label: '蚵仔煎'
  158. }, {
  159. value: '选项4',
  160. label: '龙须面'
  161. }, {
  162. value: '选项5',
  163. label: '北京烤鸭'
  164. }],
  165. value: ''
  166. };
  167. },
  168. methods: {
  169. handleChange() {
  170. console.log('changed');
  171. }
  172. }
  173. }, true);
  174. const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
  175. expect(vm.value).to.equal('');
  176. triggerEvent(options[2], 'mouseenter');
  177. options[2].click();
  178. setTimeout(() => {
  179. expect(vm.value).to.equal('选项3');
  180. expect(window.console.log.callCount).to.equal(1);
  181. options[4].click();
  182. setTimeout(() => {
  183. expect(vm.value).to.equal('选项5');
  184. expect(window.console.log.callCount).to.equal(2);
  185. window.console.log.restore();
  186. done();
  187. }, 100);
  188. }, 100);
  189. });
  190. it('disabled option', done => {
  191. vm = getSelectVm();
  192. vm.options[1].disabled = true;
  193. setTimeout(() => {
  194. const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
  195. expect(options[1].classList.contains('is-disabled')).to.true;
  196. options[1].click();
  197. setTimeout(() => {
  198. expect(vm.value).to.equal('');
  199. done();
  200. }, 100);
  201. }, 100);
  202. });
  203. it('disabled select', () => {
  204. vm = createTest(Select, { disabled: true }, true);
  205. expect(vm.$el.querySelector('.el-input').classList.contains('is-disabled')).to.true;
  206. });
  207. it('visible event', done => {
  208. vm = createVue({
  209. template: `
  210. <div>
  211. <el-select v-model="value" @visible-change="handleVisibleChange">
  212. <el-option
  213. v-for="item in options"
  214. :label="item.label"
  215. :value="item.value">
  216. </el-option>
  217. </el-select>
  218. </div>
  219. `,
  220. data() {
  221. return {
  222. options: [],
  223. value: '',
  224. visible: ''
  225. };
  226. },
  227. methods: {
  228. handleVisibleChange(val) {
  229. this.visible = val;
  230. }
  231. }
  232. }, true);
  233. vm.$children[0].visible = true;
  234. setTimeout(() => {
  235. expect(vm.visible).to.true;
  236. done();
  237. }, 50);
  238. });
  239. it('keyboard operations', done => {
  240. vm = getSelectVm();
  241. const select = vm.$children[0];
  242. let i = 8;
  243. while (i--) {
  244. select.navigateOptions('next');
  245. }
  246. select.navigateOptions('prev');
  247. setTimeout(() => {
  248. expect(select.hoverIndex).to.equal(0);
  249. select.selectOption();
  250. setTimeout(() => {
  251. expect(select.value).to.equal('选项1');
  252. done();
  253. }, 100);
  254. }, 100);
  255. });
  256. it('clearable', done => {
  257. vm = getSelectVm({ clearable: true });
  258. const select = vm.$children[0];
  259. vm.value = '选项1';
  260. select.inputHovering = true;
  261. setTimeout(() => {
  262. const icon = vm.$el.querySelector('.el-input__icon');
  263. expect(icon.classList.contains('el-icon-circle-close')).to.true;
  264. icon.click();
  265. expect(vm.value).to.equal('');
  266. done();
  267. }, 100);
  268. });
  269. it('custom el-option template', () => {
  270. vm = createVue({
  271. template: `
  272. <div>
  273. <el-select v-model="value">
  274. <el-option
  275. v-for="item in options"
  276. :label="item.label"
  277. :value="item.value">
  278. <p>{{item.label}} {{item.value}}</p>
  279. </el-option>
  280. </el-select>
  281. </div>
  282. `,
  283. data() {
  284. return {
  285. options: [{
  286. value: 'value',
  287. label: 'label'
  288. }],
  289. value: ''
  290. };
  291. }
  292. }, true);
  293. expect(vm.$el.querySelector('.el-select-dropdown__item p').textContent).to.equal('label value');
  294. });
  295. it('option group', () => {
  296. vm = createVue({
  297. template: `
  298. <div>
  299. <el-select v-model="value">
  300. <el-option-group
  301. v-for="group in options"
  302. :disabled="group.disabled"
  303. :label="group.label">
  304. <el-option
  305. v-for="item in group.options"
  306. :label="item.label"
  307. :value="item.value">
  308. </el-option>
  309. </el-option-group>
  310. </el-select>
  311. </div>
  312. `,
  313. data() {
  314. return {
  315. options: [{
  316. label: '热门城市',
  317. options: [{
  318. value: 'Shanghai',
  319. label: '上海'
  320. }, {
  321. value: 'Beijing',
  322. label: '北京'
  323. }]
  324. }, {
  325. label: '城市名',
  326. disabled: true,
  327. options: [{
  328. value: 'Chengdu',
  329. label: '成都'
  330. }, {
  331. value: 'Shenzhen',
  332. label: '深圳'
  333. }, {
  334. value: 'Guangzhou',
  335. label: '广州'
  336. }, {
  337. value: 'Dalian',
  338. label: '大连'
  339. }]
  340. }],
  341. value: ''
  342. };
  343. }
  344. }, true);
  345. const groups = vm.$el.querySelectorAll('.el-select-group__wrap');
  346. const options = groups[1].querySelectorAll('.el-select-dropdown__item');
  347. expect(groups.length).to.equal(2);
  348. expect(options.length).to.equal(4);
  349. expect(options[0].querySelector('span').textContent).to.equal('成都');
  350. });
  351. it('filterable', done => {
  352. vm = getSelectVm({ filterable: true });
  353. const select = vm.$children[0];
  354. setTimeout(() => {
  355. select.selectedLabel = '面';
  356. select.onInputChange();
  357. select.visible = true;
  358. setTimeout(() => {
  359. expect(select.filteredOptionsCount).to.equal(1);
  360. done();
  361. }, 10);
  362. }, 10);
  363. });
  364. it('filterable with custom filter-method', done => {
  365. const filterMethod = vm => {
  366. return query => {
  367. vm.options = vm.options.filter(option => option.label.indexOf(query) === -1);
  368. };
  369. };
  370. vm = getSelectVm({ filterable: true, filterMethod });
  371. const select = vm.$children[0];
  372. select.$el.querySelector('input').focus();
  373. setTimeout(() => {
  374. select.selectedLabel = '面';
  375. select.onInputChange();
  376. setTimeout(() => {
  377. expect(select.filteredOptionsCount).to.equal(4);
  378. done();
  379. }, 10);
  380. }, 10);
  381. });
  382. it('default-first-option', done => {
  383. vm = createVue({
  384. template: `
  385. <div>
  386. <el-select
  387. v-model="value"
  388. default-first-option
  389. filterable
  390. >
  391. <el-option
  392. v-for="item in options"
  393. :label="item"
  394. :value="item"
  395. />
  396. </el-select>
  397. </div>
  398. `,
  399. data() {
  400. return {
  401. options: ['1', '2', '3', '4', '5'],
  402. value: ''
  403. };
  404. },
  405. methods: {
  406. filterMethod(query) {
  407. // simulate async filterMethod / remoteMethod
  408. setTimeout(() => {
  409. this.options.filter(option => option.label.indexOf(query) !== -1);
  410. }, 5);
  411. }
  412. }
  413. }, true);
  414. const select = vm.$children[0];
  415. setTimeout(() => {
  416. select.$el.querySelector('input').focus();
  417. select.query = '3';
  418. select.selectedLabel = '3';
  419. setTimeout(() => {
  420. const enterKey = document.createEvent('Events');
  421. enterKey.initEvent('keydown', true, true);
  422. enterKey.keyCode = 13;
  423. select.$el.querySelector('input').dispatchEvent(enterKey);
  424. setTimeout(() => {
  425. expect(select.value).to.equal('3');
  426. done();
  427. }, 10);
  428. }, 10); // wait for async filterMethod
  429. }, 10);
  430. });
  431. it('allow create', done => {
  432. vm = getSelectVm({ filterable: true, allowCreate: true });
  433. const select = vm.$children[0];
  434. select.$el.querySelector('input').focus();
  435. setTimeout(() => {
  436. select.selectedLabel = 'new';
  437. select.onInputChange();
  438. setTimeout(() => {
  439. const options = document.querySelectorAll('.el-select-dropdown__item span');
  440. const target = [].filter.call(options, option => option.innerText === 'new');
  441. target[0].click();
  442. setTimeout(() => {
  443. expect(select.value.indexOf('new') > -1).to.true;
  444. done();
  445. }, 10);
  446. }, 10);
  447. }, 10);
  448. });
  449. it('multiple select', done => {
  450. vm = getSelectVm({ multiple: true });
  451. const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
  452. vm.value = ['选项1'];
  453. setTimeout(() => {
  454. options[1].click();
  455. setTimeout(() => {
  456. options[3].click();
  457. setTimeout(() => {
  458. expect(vm.value.indexOf('选项2') > -1 && vm.value.indexOf('选项4') > -1).to.true;
  459. const tagCloseIcons = vm.$el.querySelectorAll('.el-tag__close');
  460. tagCloseIcons[0].click();
  461. setTimeout(() => {
  462. expect(vm.value.indexOf('选项1')).to.equal(-1);
  463. done();
  464. }, 100);
  465. }, 100);
  466. }, 100);
  467. }, 100);
  468. });
  469. it('multiple remove-tag', done => {
  470. sinon.stub(window.console, 'log');
  471. vm = createVue({
  472. template: `
  473. <div>
  474. <el-select v-model="value" multiple @remove-tag="handleRemoveTag">
  475. <el-option
  476. v-for="item in options"
  477. :label="item.label"
  478. :value="item.value">
  479. <p>{{item.label}} {{item.value}}</p>
  480. </el-option>
  481. </el-select>
  482. </div>
  483. `,
  484. data() {
  485. return {
  486. options: [{
  487. value: '选项1',
  488. label: '黄金糕'
  489. }, {
  490. value: '选项2',
  491. label: '双皮奶'
  492. }, {
  493. value: '选项3',
  494. label: '蚵仔煎'
  495. }, {
  496. value: '选项4',
  497. label: '龙须面'
  498. }, {
  499. value: '选项5',
  500. label: '北京烤鸭'
  501. }],
  502. value: ['选项1', '选项3']
  503. };
  504. },
  505. methods: {
  506. handleRemoveTag() {
  507. console.log('remove tag');
  508. }
  509. }
  510. }, true);
  511. const tagCloseIcons = vm.$el.querySelectorAll('.el-tag__close');
  512. expect(vm.value.length).to.equal(2);
  513. tagCloseIcons[1].click();
  514. setTimeout(() => {
  515. expect(vm.value.length).to.equal(1);
  516. expect(window.console.log.callCount).to.equal(1);
  517. tagCloseIcons[0].click();
  518. setTimeout(() => {
  519. expect(vm.value.length).to.equal(0);
  520. expect(window.console.log.callCount).to.equal(2);
  521. window.console.log.restore();
  522. done();
  523. }, 100);
  524. }, 100);
  525. });
  526. it('multiple limit', done => {
  527. vm = getSelectVm({ multiple: true, multipleLimit: 1 });
  528. const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
  529. options[1].click();
  530. setTimeout(() => {
  531. expect(vm.value.indexOf('选项2') > -1).to.true;
  532. options[3].click();
  533. setTimeout(() => {
  534. expect(vm.value.indexOf('选项4')).to.equal(-1);
  535. done();
  536. }, 50);
  537. }, 50);
  538. });
  539. it('multiple remote search', done => {
  540. const remoteMethod = vm => {
  541. return query => {
  542. vm.loading = true;
  543. setTimeout(() => {
  544. vm.options = vm.options.filter(option => {
  545. return option.label.indexOf(query) > -1;
  546. });
  547. vm.loading = false;
  548. }, 200);
  549. };
  550. };
  551. vm = getSelectVm({
  552. multiple: true,
  553. remote: true,
  554. filterable: true,
  555. remoteMethod
  556. });
  557. const select = vm.$children[0];
  558. vm.$nextTick(() => {
  559. select.query = '面';
  560. setTimeout(() => {
  561. expect(select.filteredOptionsCount).to.equal(1);
  562. select.query = '';
  563. select.options[0].$el.click();
  564. vm.$nextTick(() => {
  565. expect(vm.value[0]).to.equal('选项4');
  566. select.deletePrevTag({ target: select.$refs.input });
  567. select.deletePrevTag({ target: select.$refs.input });
  568. select.resetInputState({ keyCode: 1 });
  569. vm.$nextTick(() => {
  570. expect(vm.value.length).to.equal(0);
  571. done();
  572. });
  573. });
  574. }, 250);
  575. });
  576. });
  577. it('event:focus & blur', done => {
  578. vm = createVue({
  579. template: `
  580. <el-select ref="select"></el-select>
  581. `
  582. }, true);
  583. const spyFocus = sinon.spy();
  584. const spyBlur = sinon.spy();
  585. vm.$refs.select.$on('focus', spyFocus);
  586. vm.$refs.select.$on('blur', spyBlur);
  587. vm.$el.querySelector('input').focus();
  588. vm.$el.querySelector('input').blur();
  589. vm.$nextTick(_ => {
  590. expect(spyFocus.calledOnce).to.be.true;
  591. expect(spyBlur.calledOnce).to.be.true;
  592. done();
  593. });
  594. });
  595. });