autocomplete.spec.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. import { createVue, triggerClick, destroyVM, triggerKeyDown } from '../util';
  2. describe('Autocomplete', () => {
  3. let vm;
  4. afterEach(() => {
  5. destroyVM(vm);
  6. });
  7. it('create', done => {
  8. vm = createVue({
  9. template: `
  10. <div>
  11. <button class="btn">a</button>
  12. <el-autocomplete
  13. ref="autocomplete"
  14. v-model="state"
  15. :fetch-suggestions="querySearch"
  16. placeholder="请输入内容autocomplete1"
  17. ></el-autocomplete>
  18. </div>
  19. `,
  20. data() {
  21. return {
  22. restaurants: [],
  23. state: ''
  24. };
  25. },
  26. methods: {
  27. querySearch(queryString, cb) {
  28. var restaurants = this.restaurants;
  29. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  30. cb(results);
  31. },
  32. createFilter(queryString) {
  33. return (restaurant) => {
  34. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  35. };
  36. },
  37. loadAll() {
  38. return [
  39. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  40. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  41. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  42. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
  43. ];
  44. }
  45. },
  46. mounted() {
  47. this.restaurants = this.loadAll();
  48. }
  49. }, true);
  50. let elm = vm.$el;
  51. let inputElm = elm.querySelector('input');
  52. inputElm.focus();
  53. expect(inputElm.getAttribute('placeholder')).to.be.equal('请输入内容autocomplete1');
  54. setTimeout(_ => {
  55. const suggestions = vm.$refs.autocomplete.$refs.suggestions.$el;
  56. expect(suggestions.style.display).to.not.equal('none');
  57. expect(suggestions.querySelectorAll('.el-autocomplete-suggestion__list li').length).to.be.equal(4);
  58. triggerClick(document);
  59. setTimeout(_ => {
  60. expect(suggestions.style.display).to.be.equal('none');
  61. done();
  62. }, 500);
  63. }, 500);
  64. });
  65. it('select', done => {
  66. vm = createVue({
  67. template: `
  68. <el-autocomplete
  69. v-model="state"
  70. ref="autocomplete"
  71. :fetch-suggestions="querySearch"
  72. placeholder="请输入内容autocomplete2"
  73. ></el-autocomplete>
  74. `,
  75. data() {
  76. return {
  77. restaurants: [],
  78. state: ''
  79. };
  80. },
  81. methods: {
  82. querySearch(queryString, cb) {
  83. var restaurants = this.restaurants;
  84. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  85. cb(results);
  86. },
  87. createFilter(queryString) {
  88. return (restaurant) => {
  89. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  90. };
  91. },
  92. loadAll() {
  93. return [
  94. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  95. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  96. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  97. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
  98. ];
  99. }
  100. },
  101. mounted() {
  102. this.restaurants = this.loadAll();
  103. }
  104. }, true);
  105. const autocomplete = vm.$refs.autocomplete;
  106. const elm = vm.$el;
  107. const inputElm = elm.querySelector('input');
  108. const spy = sinon.spy();
  109. autocomplete.$on('select', spy);
  110. inputElm.focus();
  111. setTimeout(_ => {
  112. const suggestions = autocomplete.$refs.suggestions.$el;
  113. const suggestionList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
  114. suggestionList[1].click();
  115. setTimeout(_ => {
  116. expect(inputElm.value).to.be.equal('Hot honey 首尔炸鸡(仙霞路)');
  117. expect(vm.state).to.be.equal('Hot honey 首尔炸鸡(仙霞路)');
  118. expect(spy.withArgs().calledOnce).to.be.true;
  119. expect(suggestions.style.display).to.be.equal('none');
  120. done();
  121. }, 500);
  122. }, 500);
  123. });
  124. it('input', done => {
  125. vm = createVue({
  126. template: `
  127. <el-autocomplete
  128. ref="autocomplete"
  129. v-model="state"
  130. :trigger-on-focus="false"
  131. :fetch-suggestions="querySearch"
  132. ></el-autocomplete>
  133. `,
  134. data() {
  135. return {
  136. restaurants: [],
  137. state: ''
  138. };
  139. },
  140. methods: {
  141. querySearch(queryString, cb) {
  142. var restaurants = this.restaurants;
  143. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  144. cb(results);
  145. },
  146. createFilter(queryString) {
  147. return (restaurant) => {
  148. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  149. };
  150. },
  151. loadAll() {
  152. return [
  153. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  154. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  155. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  156. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
  157. ];
  158. }
  159. },
  160. mounted() {
  161. this.restaurants = this.loadAll();
  162. }
  163. }, true);
  164. const autocomplete = vm.$refs.autocomplete;
  165. const input = autocomplete.$refs.input;
  166. input.$emit('input', '三');
  167. setTimeout(() => {
  168. expect(vm.state).to.be.equal('三');
  169. expect(autocomplete.suggestions[0].value).to.be.equal('三全鲜食(北新泾店)');
  170. input.$emit('input', '');
  171. setTimeout(() => {
  172. expect(vm.state).to.be.equal('');
  173. expect(autocomplete.suggestions.length).to.be.equal(0);
  174. done();
  175. }, 500);
  176. }, 500);
  177. });
  178. describe('enter select', () => {
  179. const createVm = (selectWhenUnmatched = false) => {
  180. return createVue({
  181. template: `
  182. <el-autocomplete
  183. ref="autocomplete"
  184. v-model="state"
  185. @select="handleSelect"
  186. :trigger-on-focus="false"
  187. :select-when-unmatched="selectWhenUnmatched"
  188. :fetch-suggestions="querySearch"
  189. ></el-autocomplete>
  190. `,
  191. data() {
  192. return {
  193. restaurants: [],
  194. state: '',
  195. selectWhenUnmatched: selectWhenUnmatched,
  196. item: {}
  197. };
  198. },
  199. methods: {
  200. querySearch(queryString, cb) {
  201. var restaurants = this.restaurants;
  202. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  203. cb(results);
  204. },
  205. createFilter(queryString) {
  206. return (restaurant) => {
  207. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  208. };
  209. },
  210. loadAll() {
  211. return [
  212. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  213. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  214. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  215. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
  216. ];
  217. },
  218. handleSelect(item) {
  219. this.item = item;
  220. }
  221. },
  222. mounted() {
  223. this.restaurants = this.loadAll();
  224. }
  225. }, true);
  226. };
  227. it('select', done => {
  228. vm = createVm();
  229. const autocomplete = vm.$refs.autocomplete;
  230. const input = autocomplete.$refs.input;
  231. input.$el.querySelector('input').focus();
  232. input.$emit('input', '三');
  233. setTimeout(() => {
  234. triggerKeyDown(input.$el, 40); // down
  235. setTimeout(() => {
  236. triggerKeyDown(input.$el, 13); // enter
  237. setTimeout(() => {
  238. expect(vm.item.address).to.be.equal('长宁区新渔路144号');
  239. done();
  240. }, 200);
  241. }, 200);
  242. }, 500);
  243. });
  244. it('select unmatched', done => {
  245. vm = createVm(true);
  246. const autocomplete = vm.$refs.autocomplete;
  247. const input = autocomplete.$refs.input;
  248. input.$emit('input', '关键字');
  249. setTimeout(() => {
  250. expect(autocomplete.suggestions.length).to.be.equal(0);
  251. triggerKeyDown(input.$el, 13); // enter
  252. setTimeout(() => {
  253. expect(autocomplete.highlightedIndex).to.be.equal(-1);
  254. expect(vm.item.value).to.be.equal('关键字');
  255. done();
  256. }, 500);
  257. }, 500);
  258. });
  259. });
  260. it('props', done => {
  261. vm = createVue({
  262. template: `
  263. <el-autocomplete
  264. v-model="state"
  265. ref="autocomplete"
  266. value-key="address"
  267. :fetch-suggestions="querySearch"
  268. placeholder="请输入内容autocomplete2"
  269. ></el-autocomplete>
  270. `,
  271. data() {
  272. return {
  273. restaurants: [],
  274. state: ''
  275. };
  276. },
  277. methods: {
  278. querySearch(queryString, cb) {
  279. var restaurants = this.restaurants;
  280. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  281. cb(results);
  282. },
  283. createFilter(queryString) {
  284. return (restaurant) => {
  285. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  286. };
  287. },
  288. loadAll() {
  289. return [
  290. { 'name': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  291. { 'name': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  292. { 'name': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  293. { 'name': '泷千家(天山西路店)', 'address': '天山西路438号' }
  294. ];
  295. }
  296. },
  297. mounted() {
  298. this.restaurants = this.loadAll();
  299. }
  300. }, true);
  301. const autocomplete = vm.$refs.autocomplete;
  302. const elm = vm.$el;
  303. const inputElm = elm.querySelector('input');
  304. const spy = sinon.spy();
  305. autocomplete.$on('select', spy);
  306. inputElm.focus();
  307. setTimeout(_ => {
  308. const suggestions = autocomplete.$refs.suggestions.$el;
  309. const suggestionList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
  310. expect(suggestionList[1].innerHTML === '上海市长宁区淞虹路661号');
  311. suggestionList[1].click();
  312. setTimeout(_ => {
  313. expect(inputElm.value).to.be.equal('上海市长宁区淞虹路661号');
  314. expect(vm.state).to.be.equal('上海市长宁区淞虹路661号');
  315. expect(spy.withArgs().calledOnce).to.be.true;
  316. expect(suggestions.style.display).to.be.equal('none');
  317. done();
  318. }, 500);
  319. }, 500);
  320. });
  321. it('highlight', done => {
  322. vm = createVue({
  323. template: `
  324. <el-autocomplete
  325. ref="autocomplete"
  326. v-model="state"
  327. :fetch-suggestions="querySearch"
  328. placeholder="请输入内容autocomplete3"
  329. ></el-autocomplete>
  330. `,
  331. data() {
  332. return {
  333. restaurants: [],
  334. state: ''
  335. };
  336. },
  337. methods: {
  338. querySearch(queryString, cb) {
  339. var restaurants = this.restaurants;
  340. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  341. cb(results);
  342. },
  343. createFilter(queryString) {
  344. return (restaurant) => {
  345. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  346. };
  347. },
  348. loadAll() {
  349. return [
  350. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  351. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  352. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  353. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' },
  354. { 'value': '胖仙女纸杯蛋糕(上海凌空店)', 'address': '上海市长宁区金钟路968号1幢18号楼一层商铺18-101' },
  355. { 'value': '贡茶', 'address': '上海市长宁区金钟路633号' },
  356. { 'value': '豪大大香鸡排超级奶爸', 'address': '上海市嘉定区曹安公路曹安路1685号' },
  357. { 'value': '茶芝兰(奶茶,手抓饼)', 'address': '上海市普陀区同普路1435号' },
  358. { 'value': '十二泷町', 'address': '上海市北翟路1444弄81号B幢-107' },
  359. { 'value': '星移浓缩咖啡', 'address': '上海市嘉定区新郁路817号' },
  360. { 'value': '阿姨奶茶/豪大大', 'address': '嘉定区曹安路1611号' },
  361. { 'value': '新麦甜四季甜品炸鸡', 'address': '嘉定区曹安公路2383弄55号' },
  362. { 'value': 'Monica摩托主题咖啡店', 'address': '嘉定区江桥镇曹安公路2409号1F,2383弄62号1F' },
  363. { 'value': '浮生若茶(凌空soho店)', 'address': '上海长宁区金钟路968号9号楼地下一层' },
  364. { 'value': 'NONO JUICE 鲜榨果汁', 'address': '上海市长宁区天山西路119号' },
  365. { 'value': 'CoCo都可(北新泾店)', 'address': '上海市长宁区仙霞西路' },
  366. { 'value': '快乐柠檬(神州智慧店)', 'address': '上海市长宁区天山西路567号1层R117号店铺' },
  367. { 'value': 'Merci Paul cafe', 'address': '上海市普陀区光复西路丹巴路28弄6号楼819' },
  368. { 'value': '猫山王(西郊百联店)', 'address': '上海市长宁区仙霞西路88号第一层G05-F01-1-306' },
  369. { 'value': '枪会山', 'address': '上海市普陀区棕榈路' },
  370. { 'value': '纵食', 'address': '元丰天山花园(东门) 双流路267号' },
  371. { 'value': '钱记', 'address': '上海市长宁区天山西路' }
  372. ];
  373. }
  374. },
  375. mounted() {
  376. this.restaurants = this.loadAll();
  377. }
  378. }, true);
  379. const autocomplete = vm.$refs.autocomplete;
  380. const inputElm = autocomplete.$el.querySelector('input');
  381. inputElm.focus();
  382. setTimeout(_ => {
  383. autocomplete.highlight(8);
  384. vm.$nextTick(_ => {
  385. const suggestions = autocomplete.$refs.suggestions.$el.querySelector('.el-autocomplete-suggestion__wrap');
  386. let suggestionsList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
  387. let highlightedItem = suggestionsList[8];
  388. expect(highlightedItem.classList.contains('highlighted')).to.be.true;
  389. expect(suggestions.scrollTop === highlightedItem.scrollHeight).to.be.true;
  390. done();
  391. });
  392. }, 500);
  393. });
  394. it('highlight out of bounds', done => {
  395. vm = createVue({
  396. template: `
  397. <el-autocomplete
  398. ref="autocomplete"
  399. v-model="state"
  400. :fetch-suggestions="querySearch"
  401. placeholder="请输入内容autocomplete3"
  402. ></el-autocomplete>
  403. `,
  404. data() {
  405. return {
  406. restaurants: [],
  407. state: ''
  408. };
  409. },
  410. methods: {
  411. querySearch(queryString, cb) {
  412. var restaurants = this.restaurants;
  413. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  414. cb(results);
  415. },
  416. createFilter(queryString) {
  417. return (restaurant) => {
  418. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  419. };
  420. },
  421. loadAll() {
  422. return [
  423. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  424. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  425. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  426. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' },
  427. { 'value': '胖仙女纸杯蛋糕(上海凌空店)', 'address': '上海市长宁区金钟路968号1幢18号楼一层商铺18-101' },
  428. { 'value': '贡茶', 'address': '上海市长宁区金钟路633号' },
  429. { 'value': '豪大大香鸡排超级奶爸', 'address': '上海市嘉定区曹安公路曹安路1685号' },
  430. { 'value': '茶芝兰(奶茶,手抓饼)', 'address': '上海市普陀区同普路1435号' },
  431. { 'value': '十二泷町', 'address': '上海市北翟路1444弄81号B幢-107' },
  432. { 'value': '星移浓缩咖啡', 'address': '上海市嘉定区新郁路817号' },
  433. { 'value': '阿姨奶茶/豪大大', 'address': '嘉定区曹安路1611号' },
  434. { 'value': '新麦甜四季甜品炸鸡', 'address': '嘉定区曹安公路2383弄55号' }
  435. ];
  436. }
  437. },
  438. mounted() {
  439. this.restaurants = this.loadAll();
  440. }
  441. }, true);
  442. const autocomplete = vm.$refs.autocomplete;
  443. let inputElm = vm.$el.querySelector('input');
  444. inputElm.focus();
  445. setTimeout(_ => {
  446. autocomplete.highlight(15);
  447. vm.$nextTick(_ => {
  448. const suggestions = autocomplete.$refs.suggestions.$el;
  449. const suggestionsList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
  450. let highlightedItem = suggestionsList[11];
  451. expect(highlightedItem.className).to.be.equal('highlighted');
  452. done();
  453. });
  454. }, 500);
  455. });
  456. it('triggerOnFocus', done => {
  457. vm = createVue({
  458. template: `
  459. <el-autocomplete
  460. ref="autocomplete"
  461. v-model="state"
  462. :fetch-suggestions="querySearch"
  463. :trigger-on-focus="false"
  464. placeholder="请输入内容autocomplete1"
  465. ></el-autocomplete>
  466. `,
  467. data() {
  468. return {
  469. restaurants: [],
  470. state: ''
  471. };
  472. },
  473. methods: {
  474. querySearch(queryString, cb) {
  475. var restaurants = this.restaurants;
  476. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  477. cb(results);
  478. },
  479. createFilter(queryString) {
  480. return (restaurant) => {
  481. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  482. };
  483. },
  484. loadAll() {
  485. return [
  486. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  487. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  488. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  489. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
  490. ];
  491. }
  492. },
  493. mounted() {
  494. this.restaurants = this.loadAll();
  495. }
  496. }, true);
  497. let inputElm = vm.$el.querySelector('input');
  498. inputElm.focus();
  499. setTimeout(_ => {
  500. let suggestions = vm.$refs.autocomplete.$refs.suggestions.$el;
  501. expect(suggestions.style.display).to.be.equal('none');
  502. done();
  503. }, 500);
  504. });
  505. it('event:focus & blur', done => {
  506. vm = createVue({
  507. template: `
  508. <el-autocomplete
  509. ref="input"
  510. v-model="state"
  511. :fetch-suggestions="querySearch"
  512. :trigger-on-focus="false"
  513. placeholder="请输入内容autocomplete1"
  514. ></el-autocomplete>
  515. `,
  516. data() {
  517. return {
  518. restaurants: [],
  519. state: ''
  520. };
  521. },
  522. methods: {
  523. querySearch(queryString, cb) {
  524. var restaurants = this.restaurants;
  525. var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
  526. cb(results);
  527. },
  528. createFilter(queryString) {
  529. return (restaurant) => {
  530. return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
  531. };
  532. },
  533. loadAll() {
  534. return [
  535. { 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
  536. { 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
  537. { 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
  538. { 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
  539. ];
  540. }
  541. },
  542. mounted() {
  543. this.restaurants = this.loadAll();
  544. }
  545. }, true);
  546. const spyFocus = sinon.spy();
  547. const spyBlur = sinon.spy();
  548. vm.$refs.input.$on('focus', spyFocus);
  549. vm.$refs.input.$on('blur', spyBlur);
  550. vm.$el.querySelector('input').focus();
  551. vm.$el.querySelector('input').blur();
  552. vm.$nextTick(_ => {
  553. expect(spyFocus.calledOnce).to.be.true;
  554. expect(spyBlur.calledOnce).to.be.true;
  555. done();
  556. });
  557. });
  558. });