test-ido-completing-read+.el 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. ;;; -*- lexical-binding: t -*-
  2. (require 'undercover)
  3. (undercover "*.el"
  4. (:exclude "test-*.el"))
  5. (require 'ido)
  6. (require 'flx-ido)
  7. (require 'minibuf-eldef)
  8. (require 'ido-completing-read+)
  9. (require 'buttercup)
  10. (require 'cl-lib)
  11. (require 'with-simulated-input)
  12. (defun collection-as-function (collection)
  13. "Return a function equivalent to COLLECTION.
  14. The returned function will work equivalently to COLLECTION when
  15. passed to `all-completions' and `try-completion'."
  16. (completion-table-dynamic (lambda (string) (all-completions string collection))))
  17. (defun shadow-var (var &optional temp-value)
  18. "Shadow the value of VAR.
  19. This will push the current value of VAR to VAR's
  20. `shadowed-values' property, and then set it to TEMP-VALUE. To
  21. reverse this process, call `unshadow-var' on VAR. Vars can
  22. be shadowed recursively, and must be unshadowed once for each
  23. shadowing in order to restore the original value. You can think
  24. of shadowing as dynamic binding with `let', but with manual
  25. control over when bindings start and end.
  26. If VAR is a Custom variable (see `custom-variable-p'), it will be
  27. set using `customize-set-variable', and if TEMP-VALUE is nil it
  28. will be replaces with VAR's standard value.
  29. Other variables will be set with `set-default', and a TEMP-VALUE
  30. of nil will not be treated specially.
  31. `shadow-var' only works on variables declared as special (i.e.
  32. using `defvar' or similar). It will not work on lexically bound
  33. variables."
  34. (unless (special-variable-p var)
  35. (error "Cannot shadow lexical var `%s'" var))
  36. (let* ((use-custom (custom-variable-p var))
  37. (setter (if use-custom 'customize-set-variable 'set-default))
  38. (temp-value (or temp-value
  39. (and use-custom
  40. (eval (car (get var 'standard-value)))))))
  41. ;; Push the current value on the stack
  42. (push (symbol-value var) (get var 'shadowed-values))
  43. (funcall setter var temp-value)))
  44. (defun var-shadowed-p (var)
  45. "Return non-nil if VAR is shadowed by `shadow-var'."
  46. ;; We don't actually want to return that list if it's non-nil.
  47. (and (get var 'shadowed-values) t))
  48. (defun unshadow-var (var)
  49. "Reverse the last call to `shadow-var' on VAR."
  50. (if (var-shadowed-p var)
  51. (let* ((use-custom (custom-variable-p var))
  52. (setter (if use-custom 'customize-set-variable 'set-default))
  53. (value (pop (get var 'shadowed-values))))
  54. (funcall setter var value))
  55. (error "Var is not shadowed: %s" var)))
  56. (defun fully-unshadow-var (var)
  57. "Reverse *all* calls to `shadow-var' on VAR."
  58. (when (var-shadowed-p var)
  59. (let* ((use-custom (custom-variable-p var))
  60. (setter (if use-custom 'customize-set-variable 'set-default))
  61. (value (car (last (get var 'shadowed-values)))))
  62. (put var 'shadowed-values nil)
  63. (funcall setter var value))))
  64. (defun fully-unshadow-all-vars (&optional vars)
  65. "Reverse *all* calls to `shadow-var' on VARS.
  66. If VARS is nil, unshadow *all* variables."
  67. (if vars
  68. (mapc #'fully-unshadow-var vars)
  69. (mapatoms #'fully-unshadow-var))
  70. nil)
  71. (defmacro shadow-vars (varlist)
  72. "Shadow a list of vars with new values.
  73. VARLIST describes the variables to be shadowed with the same
  74. syntax as `let'.
  75. See `shadow-var'."
  76. (declare (indent 0))
  77. (cl-loop
  78. with var = nil
  79. with value = nil
  80. for binding in varlist
  81. if (symbolp binding)
  82. do (setq var binding
  83. value nil)
  84. else
  85. do (setq var (car binding)
  86. value (cadr binding))
  87. collect `(shadow-var ',var ,value) into exprs
  88. finally return `(progn ,@exprs)))
  89. (defmacro unshadow-vars (vars)
  90. "Un-shadow a list of VARS.
  91. This is a macro for consistency with `shadow-vars', but it will
  92. also accept a quoted list for the sake of convenience."
  93. (declare (indent 0))
  94. (when (eq (car vars) 'quote)
  95. (setq vars (eval vars)))
  96. `(mapc #'unshadow-var ',vars))
  97. (describe "Within the `ido-completing-read+' package"
  98. ;; Reset all of these variables to their standard values before each
  99. ;; test, saving the previous values for later restoration.
  100. (before-each
  101. (shadow-vars
  102. ((ido-mode t)
  103. (ido-ubiquitous-mode t)
  104. (ido-cr+-debug-mode t)
  105. ido-cr+-auto-update-blacklist
  106. ido-cr+-fallback-function
  107. ido-cr+-max-items
  108. ido-cr+-function-blacklist
  109. ido-cr+-function-whitelist
  110. ido-cr+-nil-def-alternate-behavior-list
  111. ido-cr+-replace-completely
  112. ido-confirm-unique-completion
  113. ido-enable-flex-matching
  114. ido-enable-dot-prefix
  115. flx-ido-mode
  116. (minibuffer-electric-default-mode t)))
  117. ;; Suppress all messages during tests
  118. (spy-on 'message))
  119. ;; Restore the saved values after each test
  120. (after-each
  121. (fully-unshadow-all-vars))
  122. (describe "the `ido-completing-read+' function"
  123. (it "should complete with a matching item on RET"
  124. (expect
  125. (with-simulated-input "g RET"
  126. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  127. :to-equal "green"))
  128. (it "should complete with the first match when multiple matches are available"
  129. (expect
  130. (with-simulated-input "b RET"
  131. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
  132. :to-equal "brown"))
  133. (it "should allow <left> and <right> to cycle completions, with wrap-around"
  134. (expect
  135. (with-simulated-input "b <right> <right> <right> <right> <left> RET"
  136. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
  137. :to-equal
  138. "blue"))
  139. (it "should return \"\" when RET or C-j is pressed on an empty input even when REQUIRE-MATCH is non-nil"
  140. ;; No REQUIRE-MATCH
  141. (expect
  142. (with-simulated-input "RET"
  143. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  144. :to-equal "blue")
  145. (expect
  146. (with-simulated-input "C-j"
  147. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  148. :to-equal "")
  149. ;; Again, with REQUIRE-MATCH
  150. (expect
  151. (with-simulated-input "RET"
  152. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
  153. :to-equal "")
  154. (expect
  155. (with-simulated-input "C-j"
  156. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
  157. :to-equal ""))
  158. (it "should accept all the same forms of DEF as `completing-read-default'"
  159. ;; DEF in COLLECTION
  160. (expect
  161. (with-simulated-input "RET"
  162. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "green"))
  163. :to-equal "green")
  164. ;; Same, with REQUIRE-MATCH
  165. (expect
  166. (with-simulated-input "RET"
  167. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "green"))
  168. :to-equal "green")
  169. ;; DEF not in COLLECTION
  170. (expect
  171. (with-simulated-input "RET"
  172. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "brown"))
  173. :to-equal "brown")
  174. ;; Same, with REQUIRE-MATCH
  175. (expect
  176. (with-simulated-input "RET"
  177. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "brown"))
  178. :to-equal "brown")
  179. ;; List DEF, partially in COLLECTION
  180. (expect
  181. (with-simulated-input "RET"
  182. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil '("brown" "green")))
  183. :to-equal "brown"))
  184. (it "should work with INITIAL-INPUT"
  185. (expect
  186. (with-simulated-input "RET"
  187. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr"))
  188. :to-equal "green"))
  189. (it "should properly handle a cons INITIAL-INPUT"
  190. (expect
  191. (with-simulated-input "ee RET"
  192. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil (cons "gr" 2)))
  193. :to-equal "green"))
  194. (it "should properly handle both INITIAL-INPUT and DEF at the same time"
  195. (expect
  196. (with-simulated-input "RET"
  197. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
  198. :to-equal "green")
  199. (expect
  200. (with-simulated-input "DEL DEL RET"
  201. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
  202. :to-equal "blue"))
  203. (it "should work when COLLECTION is a function"
  204. (expect
  205. (with-simulated-input "g RET"
  206. (ido-completing-read+ "Prompt: " (collection-as-function '("blue" "yellow" "green"))))
  207. :to-equal "green"))
  208. (it "should fall back when COLLECTION is empty"
  209. (spy-on 'ido-completing-read :and-call-through)
  210. (expect
  211. (with-simulated-input "g RET"
  212. (ido-completing-read+ "Prompt: " nil))
  213. :to-equal "g")
  214. (expect 'ido-completing-read :not :to-have-been-called))
  215. (it "should replace `ido-completing-read' when `ido-cr+-replace-completely' is non-nil"
  216. (customize-set-variable 'ido-cr+-replace-completely t)
  217. (spy-on 'ido-completing-read+ :and-call-through)
  218. (expect
  219. (with-simulated-input "g RET"
  220. (ido-completing-read "Prompt: " '("blue" "yellow" "green")))
  221. :to-equal "green")
  222. (expect 'ido-completing-read+ :to-have-been-called))
  223. (describe "when `ido-cr+-max-items' is set"
  224. (it "should not trigger a fallback for small collections"
  225. (expect
  226. (with-simulated-input "g RET"
  227. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  228. :to-equal "green"))
  229. (it "should trigger a fallback for large collections"
  230. (expect
  231. ;; With max-items negative, all collections are considered "too
  232. ;; large"
  233. (let ((ido-cr+-max-items -1))
  234. (with-simulated-input "g RET"
  235. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green"))))
  236. :to-equal "g")))
  237. (describe "when REQUIRE-MATCH is non-nil"
  238. (it "should still allow exiting with an empty string if DEF is nil"
  239. (expect
  240. (with-simulated-input "C-j"
  241. (ido-completing-read+
  242. "Prompt: "
  243. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  244. :to-equal ""))
  245. ;; "C-j" should NOT be allowed to return an empty string if
  246. ;; require-match and default are both non-nil.
  247. (it "should not alow exiting with an empty string if DEF is non-nil"
  248. (expect
  249. (with-simulated-input "C-j"
  250. (ido-completing-read+
  251. "Prompt: "
  252. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t nil nil "yellow"))
  253. :to-throw))
  254. (it "shouldn't allow C-j to select an ambiguous match"
  255. (expect
  256. (with-simulated-input "b C-j C-j C-j"
  257. (ido-completing-read+
  258. "Prompt: "
  259. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  260. :to-throw)
  261. ;; First press of C-j should complete to "blue" after the
  262. ;; first b, but then get stuck on the choice for the second b.
  263. (expect
  264. (with-simulated-input "b C-j b C-j C-j C-j"
  265. (ido-completing-read+
  266. "Prompt: "
  267. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  268. :to-throw))
  269. (it "should allow exiting with an unambiguous match"
  270. (expect
  271. (with-simulated-input "b C-j b C-j e C-j C-j"
  272. (ido-completing-read+
  273. "Prompt: "
  274. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  275. :to-equal "blueberry")
  276. ;; The "C-j" should complete to "bluegrass" and return, because
  277. ;; `ido-confirm-unique-completion is nil.
  278. (expect
  279. (with-simulated-input "b l u e g C-j"
  280. (ido-completing-read+
  281. "Prompt: "
  282. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  283. :to-equal "bluegrass"))
  284. (it "should require an extra C-j to exit when `ido-confirm-unique-completion' is non-nil"
  285. (setq ido-confirm-unique-completion t)
  286. ;; Now the first "C-j" should complete to "bluegrass" but should
  287. ;; not return.
  288. (expect
  289. (with-simulated-input "b l u e g C-j"
  290. (ido-completing-read+
  291. "Prompt: "
  292. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  293. :to-throw)
  294. ;; The first "C-j" should complete to "bluegrass", and the second
  295. ;; should return.
  296. (expect
  297. (with-simulated-input "b l u e g C-j C-j"
  298. (ido-completing-read+
  299. "Prompt: "
  300. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  301. :to-equal "bluegrass"))
  302. ;; Finally, a test for the expected wrong behavior without
  303. ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
  304. ;; to fail as a signal that the workaround can be phased out.
  305. (it "should return a non-match when ordinary `ido-completing-read' is used"
  306. (expect
  307. (with-simulated-input "b C-j"
  308. (ido-completing-read
  309. "Prompt: "
  310. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  311. :to-equal "b")))
  312. (describe "when INHERIT-INPUT-METHOD is non-nil"
  313. (before-each
  314. (spy-on 'ido-completing-read :and-call-through))
  315. (it "should not fall back if `current-input-method' is non-nil"
  316. (expect
  317. (let ((current-input-method nil))
  318. (with-simulated-input "g RET"
  319. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  320. :to-equal "green"))
  321. (expect 'ido-completing-read :to-have-been-called))
  322. (it "should not fall back if `current-input-method' is non-nil"
  323. (expect
  324. (let ((current-input-method 'ucs))
  325. (with-simulated-input "g RET"
  326. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  327. :to-equal "green"))
  328. (expect 'ido-completing-read :not :to-have-been-called)))
  329. (describe "with manual fallback shortcuts"
  330. (it "should not fall back when C-b or C-f is used in the middle of the input"
  331. (expect
  332. ;; C-b/f not at beginning/end of input should not fall back
  333. (with-simulated-input "g C-b C-f RET"
  334. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  335. :to-equal "green"))
  336. (it "should fall back on C-f at end of input"
  337. (expect
  338. ;; C-f at end of input should fall back
  339. (with-simulated-input "g C-f RET"
  340. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  341. :to-equal "g"))
  342. (it "should not fall back from repeated C-b that hits the start of input"
  343. (expect
  344. ;; Repeated C-b should not fall back
  345. (with-simulated-input "g C-b C-b C-b C-b RET"
  346. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  347. :to-equal "green"))
  348. (it "should fall back on C-b at beginning of input (if previous action was not C-b)"
  349. (expect
  350. ;; C-b at beginning of line should fall back (if previous action
  351. ;; was not also C-b)
  352. (with-simulated-input "g C-b x DEL C-b RET"
  353. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  354. :to-equal "g")))
  355. (describe "with a workaround for an bug with non-nil `ido-enable-dot-prefix'"
  356. ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997
  357. ;; for more information on this bug.
  358. (before-each
  359. (setq ido-enable-dot-prefix t))
  360. (it "should not throw an error when \"\" is in the collection"
  361. (expect
  362. (with-simulated-input "RET"
  363. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  364. :to-equal "")
  365. (expect
  366. (with-simulated-input "a a b RET"
  367. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  368. :to-equal "aab")))
  369. (describe "with dynamic collections"
  370. (before-all
  371. (setq my-dynamic-collection
  372. (completion-table-dynamic
  373. (lambda (text)
  374. (cond
  375. ;; Sub-completions for "hello"
  376. ((s-prefix-p "hello" text)
  377. '("hello" "hello-world" "hello-everyone" "hello-universe"))
  378. ;; Sub-completions for "goodbye"
  379. ((s-prefix-p "goodbye" text)
  380. '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
  381. ;; General completions
  382. (t
  383. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill")))))))
  384. (after-all
  385. (setq my-dynamic-collection nil))
  386. (before-each
  387. (setq ido-enable-flex-matching t
  388. ido-confirm-unique-completion nil)
  389. (spy-on 'ido-cr+-update-dynamic-collection
  390. :and-call-through))
  391. (it "should allow selection of dynamically-added completions"
  392. (expect
  393. (with-simulated-input "hello- RET"
  394. (ido-completing-read+ "Say something: " my-dynamic-collection))
  395. :to-equal "hello-world")
  396. (expect 'ido-cr+-update-dynamic-collection
  397. :to-have-been-called))
  398. (it "should allow ido flex-matching of dynamically-added completions"
  399. (expect
  400. (with-simulated-input "hello-ld RET"
  401. (ido-completing-read+ "Say something: " my-dynamic-collection))
  402. :to-equal
  403. "hello-world")
  404. (expect 'ido-cr+-update-dynamic-collection
  405. :to-have-been-called))
  406. (it "should do a dynamic update when pressing TAB"
  407. (expect
  408. (with-simulated-input "h TAB -ld RET"
  409. (ido-completing-read+ "Say something: " my-dynamic-collection))
  410. :to-equal
  411. "hello-world")
  412. (expect 'ido-cr+-update-dynamic-collection
  413. :to-have-been-called))
  414. (it "should do a dynamic update when idle"
  415. (expect
  416. (with-simulated-input
  417. '("h"
  418. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  419. "-ld RET")
  420. (ido-completing-read+ "Say something: " my-dynamic-collection))
  421. :to-equal
  422. "hello-world")
  423. (expect 'ido-cr+-update-dynamic-collection
  424. :to-have-been-called))
  425. (it "should do a dynamic update when there is only one match remaining"
  426. (expect
  427. (with-simulated-input "hell-ld RET"
  428. (ido-completing-read+ "Say something: " my-dynamic-collection))
  429. :to-equal
  430. "hello-world")
  431. (expect 'ido-cr+-update-dynamic-collection
  432. :to-have-been-called))
  433. (it "should not exit with a unique match if new matches are dynamically added"
  434. (expect
  435. (with-simulated-input '("hell TAB -ld RET")
  436. (ido-completing-read+ "Say something: " my-dynamic-collection))
  437. :to-equal
  438. "hello-world")
  439. (expect 'ido-cr+-update-dynamic-collection
  440. :to-have-been-called))
  441. (it "should exit with a match that is still unique after dynamic updating"
  442. (expect
  443. (with-simulated-input '("helic TAB")
  444. (ido-completing-read+ "Say something: " my-dynamic-collection))
  445. :to-equal
  446. "helicopter")
  447. (expect 'ido-cr+-update-dynamic-collection
  448. :to-have-been-called))
  449. (it "should suppress errors raised by dynamic completion updates"
  450. (let ((collection
  451. (completion-table-dynamic
  452. (lambda (text)
  453. (cond
  454. ((equal text "")
  455. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill"))
  456. (t (error "This collection throws an error on a nonempty prefix")))))))
  457. (expect
  458. (with-simulated-input '("hell TAB RET")
  459. (ido-completing-read+ "Say something: " collection))
  460. :to-equal
  461. "hello")))
  462. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  463. (assume (version<= "25" emacs-version))
  464. (let ((collection
  465. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  466. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  467. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  468. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  469. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  470. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  471. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  472. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  473. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  474. ;; Test the internal function
  475. (expect
  476. (ido-cr+-apply-restrictions
  477. collection
  478. (list (cons nil "bbb")
  479. (cons nil "eee")))
  480. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  481. ;; First verify it without a dynamic collection
  482. (expect
  483. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  484. (ido-completing-read+
  485. "Pick: " collection nil t nil nil (car collection)))
  486. :to-equal "bbb-eee-ggg")
  487. (expect
  488. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  489. (ido-completing-read+
  490. "Pick: " collection nil t nil nil (car collection)))
  491. :to-equal "bbb-eee-ggg")
  492. ;; Now test the same with a dynamic collection
  493. (expect
  494. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  495. (ido-completing-read+
  496. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  497. :to-equal "bbb-eee-ggg")
  498. (expect
  499. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  500. (ido-completing-read+
  501. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  502. :to-equal "bbb-eee-ggg")))
  503. (describe "with flx-ido-mode"
  504. (before-each
  505. (flx-ido-mode 1)
  506. (flx-ido-reset))
  507. (it "should allow selection of dynamically-added completions"
  508. (expect
  509. (with-simulated-input "hello-w RET"
  510. (ido-completing-read+ "Say something: " my-dynamic-collection))
  511. :to-equal "hello-world")
  512. (expect 'ido-cr+-update-dynamic-collection
  513. :to-have-been-called))
  514. (it "should allow ido flex-matching of dynamically-added completions"
  515. (expect
  516. (with-simulated-input "hello-ld RET"
  517. (ido-completing-read+ "Say something: " my-dynamic-collection))
  518. :to-equal
  519. "hello-world")
  520. (expect 'ido-cr+-update-dynamic-collection
  521. :to-have-been-called))
  522. (it "should do a dynamic update when pressing TAB"
  523. (expect
  524. (with-simulated-input "h TAB -ld RET"
  525. (ido-completing-read+ "Say something: " my-dynamic-collection))
  526. :to-equal
  527. "hello-world")
  528. (expect 'ido-cr+-update-dynamic-collection
  529. :to-have-been-called))
  530. (it "should do a dynamic update when idle"
  531. (expect
  532. (with-simulated-input
  533. '("h"
  534. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  535. "-ld RET")
  536. (ido-completing-read+ "Say something: " my-dynamic-collection))
  537. :to-equal
  538. "hello-world")
  539. (expect 'ido-cr+-update-dynamic-collection
  540. :to-have-been-called))
  541. (it "should do a dynamic update when there is only one match remaining"
  542. (expect
  543. (with-simulated-input "hell-ld RET"
  544. (ido-completing-read+ "Say something: " my-dynamic-collection))
  545. :to-equal
  546. "hello-world")
  547. (expect 'ido-cr+-update-dynamic-collection
  548. :to-have-been-called))
  549. (it "should not exit with a unique match if new matches are dynamically added"
  550. (expect
  551. (with-simulated-input '("hell TAB -ld RET")
  552. (ido-completing-read+ "Say something: " my-dynamic-collection))
  553. :to-equal
  554. "hello-world")
  555. (expect 'ido-cr+-update-dynamic-collection
  556. :to-have-been-called))
  557. (it "should exit with a match that is still unique after dynamic updating"
  558. (expect
  559. (with-simulated-input '("helic TAB")
  560. (ido-completing-read+ "Say something: " my-dynamic-collection))
  561. :to-equal
  562. "helicopter")
  563. (expect 'ido-cr+-update-dynamic-collection
  564. :to-have-been-called))
  565. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  566. (let ((collection
  567. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  568. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  569. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  570. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  571. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  572. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  573. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  574. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  575. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  576. ;; Test the internal function
  577. (expect
  578. (ido-cr+-apply-restrictions
  579. collection
  580. (list (cons nil "bbb")
  581. (cons nil "eee")))
  582. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  583. ;; First verify it without a dynamic collection
  584. (expect
  585. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  586. (ido-completing-read+
  587. "Pick: " collection nil t nil nil (car collection)))
  588. :to-equal "bbb-eee-ggg")
  589. ;; Now test the same with a dynamic collection
  590. (expect
  591. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  592. (ido-completing-read+
  593. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  594. :to-equal "bbb-eee-ggg")))))
  595. (describe "with unusual inputs"
  596. (it "should accept a COLLECTION of symbols"
  597. (expect
  598. (with-simulated-input "g RET"
  599. (ido-completing-read+ "Prompt: " '(blue yellow green)))
  600. :to-equal "green"))
  601. (it "should accept a mix of strings and symbols in COLLECTION"
  602. (expect
  603. (with-simulated-input "g RET"
  604. (ido-completing-read+ "Prompt: " '(blue "yellow" green)))
  605. :to-equal "green"))
  606. (it "should accept symbols in DEF"
  607. (expect
  608. (with-simulated-input "RET"
  609. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '(brown "green")))
  610. :to-equal "brown"))
  611. (it "should accept an alist COLLECTION"
  612. (expect
  613. (with-simulated-input "RET"
  614. (ido-completing-read+
  615. "Prompt: "
  616. '(("blue" . blue-value)
  617. ("yellow" . yellow-value)
  618. (green . green-value))
  619. nil nil nil nil "green"))
  620. :to-equal "green"))
  621. (it "should accept a hash table COLLECTION"
  622. (expect
  623. (with-simulated-input "RET"
  624. (let ((collection (make-hash-table)))
  625. (puthash "blue" 'blue-value collection)
  626. (puthash "yellow" 'yellow-value collection)
  627. (puthash 'green 'green-value collection)
  628. (ido-completing-read+ "Prompt: " collection nil nil nil nil "green")))
  629. :to-equal "green"))
  630. (it "should accept an obarray COLLECTION"
  631. (expect
  632. (with-simulated-input "forward-char RET"
  633. (ido-completing-read+ "Prompt: " obarray #'commandp
  634. t nil nil "backward-char"))
  635. :to-equal "forward-char"))))
  636. (describe "ido-ubiquitous-mode"
  637. ;; Set up a test command that calls `completing-read'
  638. (before-all
  639. (setf (symbol-function 'test-command)
  640. (lambda ()
  641. (interactive)
  642. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  643. ;; Delete the test command
  644. (after-all
  645. (setf (symbol-function 'test-command) nil))
  646. ;; Verify that the mode can be activated
  647. (it "should enable itself properly"
  648. (expect
  649. (progn
  650. (ido-ubiquitous-mode 1)
  651. (with-simulated-input "g RET"
  652. (command-execute 'test-command)))
  653. :to-equal "green"))
  654. (it "should disable itself properly"
  655. (expect
  656. (progn
  657. (ido-ubiquitous-mode 0)
  658. (with-simulated-input "g RET"
  659. (command-execute 'test-command)))
  660. :to-equal "g"))
  661. (describe "with `ido-cr+-function-blacklist'"
  662. (before-all
  663. (setf (symbol-function 'blacklisted-command)
  664. (lambda (arg)
  665. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  666. arg)
  667. (symbol-function 'blacklisted-function)
  668. (lambda ()
  669. (completing-read "Prompt: " '("blue" "yellow" "green")))
  670. (symbol-function 'cmd-that-calls-blacklisted-function)
  671. (lambda ()
  672. (interactive)
  673. (funcall 'blacklisted-function))
  674. (symbol-function 'blacklisted-collection)
  675. (collection-as-function '("blue" "yellow" "green"))))
  676. (after-all
  677. (setf (symbol-function 'blacklisted-command) nil
  678. (symbol-function 'blacklisted-function) nil
  679. (symbol-function 'cmd-that-calls-blacklisted-function) nil
  680. (symbol-function 'blacklisted-collection) nil))
  681. ;; First verify that they work normally before blacklisting them
  682. (describe "when the specified functions are not blacklisted"
  683. (it "should not affect a non-blacklisted command"
  684. (expect
  685. (with-simulated-input "g RET"
  686. (call-interactively 'blacklisted-command))
  687. :to-equal "green"))
  688. (it "should not affect a non-blacklisted function"
  689. (expect
  690. (with-simulated-input "g RET"
  691. (call-interactively 'cmd-that-calls-blacklisted-function))
  692. :to-equal "green"))
  693. (it "should not affect a non-blacklisted collection"
  694. (expect
  695. (with-simulated-input "g RET"
  696. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  697. :to-equal "green")))
  698. (describe "when the specified functions are blacklisted"
  699. (before-each
  700. (setq ido-cr+-function-blacklist
  701. (append '(blacklisted-command
  702. blacklisted-function
  703. blacklisted-collection)
  704. ido-cr+-function-blacklist)))
  705. (it "should prevent ido in a blacklisted command"
  706. (expect
  707. (with-simulated-input "g RET"
  708. (call-interactively 'blacklisted-command))
  709. :to-equal "g"))
  710. (it "should prevent ido in a blacklisted function"
  711. (expect
  712. (with-simulated-input "g RET"
  713. (call-interactively 'cmd-that-calls-blacklisted-function))
  714. :to-equal "g"))
  715. (it "should prevent ido with a blacklisted collection"
  716. (expect
  717. (with-simulated-input "g RET"
  718. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  719. :to-equal "g")))
  720. (describe "when updating ido-cr+"
  721. (before-each
  722. (spy-on 'ido-cr+-update-blacklist :and-call-through))
  723. (it "should update the blacklist when `ido-cr+-auto-update-blacklist' is t"
  724. (assume ido-cr+-function-blacklist)
  725. (let ((orig-blacklist ido-cr+-function-blacklist))
  726. (customize-set-variable 'ido-cr+-auto-update-blacklist t)
  727. (customize-set-variable 'ido-cr+-function-blacklist nil)
  728. (ido-cr+-maybe-update-blacklist)
  729. (expect 'ido-cr+-update-blacklist :to-have-been-called)
  730. (expect ido-cr+-function-blacklist :to-have-same-items-as orig-blacklist)))
  731. (it "should not update the blacklist when `ido-cr+-auto-update-blacklist' is nil"
  732. (assume ido-cr+-function-blacklist)
  733. (let ((orig-blacklist ido-cr+-function-blacklist))
  734. (customize-set-variable 'ido-cr+-auto-update-blacklist nil)
  735. (customize-set-variable 'ido-cr+-function-blacklist nil)
  736. (ido-cr+-maybe-update-blacklist)
  737. (expect 'ido-cr+-update-blacklist :not :to-have-been-called)
  738. (expect ido-cr+-function-blacklist :to-have-same-items-as nil)))
  739. (it "should notify about blacklist updates when `ido-cr+-auto-update-blacklist' is `notify'"
  740. (assume ido-cr+-function-blacklist)
  741. (spy-on 'display-warning)
  742. (let ((orig-blacklist ido-cr+-function-blacklist))
  743. (customize-set-variable 'ido-cr+-auto-update-blacklist 'notify)
  744. (customize-set-variable 'ido-cr+-function-blacklist nil)
  745. (ido-cr+-maybe-update-blacklist)
  746. (expect 'ido-cr+-update-blacklist :not :to-have-been-called)
  747. (expect 'display-warning :to-have-been-called)
  748. (expect ido-cr+-function-blacklist :to-have-same-items-as nil)))))
  749. (describe "with `ido-cr+-function-whitelist'"
  750. (before-all
  751. (setf (symbol-function 'whitelisted-command)
  752. (lambda (arg)
  753. (interactive
  754. (list
  755. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  756. arg)
  757. (symbol-function 'whitelisted-function)
  758. (lambda ()
  759. (completing-read "Prompt: " '("blue" "yellow" "green")))
  760. (symbol-function 'cmd-that-calls-whitelisted-function)
  761. (lambda ()
  762. (interactive)
  763. (funcall 'whitelisted-function))
  764. (symbol-function 'whitelisted-collection)
  765. (lambda (string pred action)
  766. (complete-with-action action '("blue" "yellow" "green") string pred))))
  767. (after-all
  768. (setf (symbol-function 'whitelisted-command) nil
  769. (symbol-function 'whitelisted-function) nil
  770. (symbol-function 'cmd-that-calls-whitelisted-function) nil
  771. (symbol-function 'whitelisted-collection) nil))
  772. (describe "when the whitelist is inactive (i.e. everything is whitelisted)"
  773. (before-each
  774. (setq ido-cr+-function-whitelist nil))
  775. (it "should enable ido in a command"
  776. (expect
  777. (with-simulated-input "g RET"
  778. (call-interactively 'whitelisted-command))
  779. :to-equal "green"))
  780. (it "should enable ido in a function"
  781. (expect
  782. (with-simulated-input "g RET"
  783. (call-interactively 'cmd-that-calls-whitelisted-function))
  784. :to-equal "green"))
  785. (it "should enable ido for a collection"
  786. (expect
  787. (with-simulated-input "g RET"
  788. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  789. :to-equal "green")))
  790. (describe "when the specified functions are whitelisted"
  791. (before-each
  792. (setq ido-cr+-function-whitelist
  793. (append '(whitelisted-command
  794. whitelisted-function
  795. whitelisted-collection)
  796. ido-cr+-function-whitelist)))
  797. (it "should enable ido in a whitelisted command"
  798. (expect
  799. (with-simulated-input "g RET"
  800. (call-interactively 'whitelisted-command))
  801. :to-equal "green"))
  802. (it "should enable ido in a whitelisted function"
  803. (expect
  804. (with-simulated-input "g RET"
  805. (call-interactively 'cmd-that-calls-whitelisted-function))
  806. :to-equal "green"))
  807. (it "should enable ido for a whitelisted collection"
  808. (expect
  809. (with-simulated-input "g RET"
  810. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  811. :to-equal "green")))
  812. (describe "when the whitelist is active but empty (i.e. nothing whitelisted)"
  813. (before-each
  814. (setq ido-cr+-function-whitelist (list nil)))
  815. (it "should prevent ido in a command"
  816. (expect
  817. (with-simulated-input "g RET"
  818. (call-interactively 'whitelisted-command))
  819. :to-equal "g"))
  820. (it "should prevent ido in a function"
  821. (expect
  822. (with-simulated-input "g RET"
  823. (call-interactively 'cmd-that-calls-whitelisted-function))
  824. :to-equal "g"))
  825. (it "should prevent ido for a collection"
  826. (expect
  827. (with-simulated-input "g RET"
  828. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  829. :to-equal "g"))))
  830. (describe "with `ido-cr+-nil-def-alternate-behavior-list'"
  831. (before-all
  832. (setf (symbol-function 'def-nil-command)
  833. (lambda (arg)
  834. (interactive
  835. (list
  836. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))
  837. arg)
  838. (symbol-function 'def-nil-function)
  839. (lambda ()
  840. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  841. (symbol-function 'cmd-that-calls-def-nil-function)
  842. (lambda ()
  843. (interactive)
  844. (funcall 'def-nil-function))
  845. (symbol-function 'def-nil-collection)
  846. (lambda (string pred action)
  847. (complete-with-action action '("blue" "yellow" "green") string pred))))
  848. (after-all
  849. (setf (symbol-function 'def-nil-command) nil
  850. (symbol-function 'def-nil-function) nil
  851. (symbol-function 'cmd-that-calls-def-nil-function) nil
  852. (symbol-function 'def-nil-collection) nil))
  853. (describe "when the specified functions are not in the list"
  854. (before-each
  855. (setq ido-cr+-nil-def-alternate-behavior-list nil))
  856. (it "should use empty string default in a command"
  857. (expect
  858. (with-simulated-input "RET"
  859. (call-interactively 'def-nil-command))
  860. :to-equal ""))
  861. (it "should use empty string default in a function"
  862. (expect
  863. (with-simulated-input "RET"
  864. (call-interactively 'cmd-that-calls-def-nil-function))
  865. :to-equal ""))
  866. (it "should use empty string default for a collection"
  867. (expect
  868. (with-simulated-input "RET"
  869. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  870. :to-equal "")))
  871. (describe "when the specified functions are in the list"
  872. (before-each
  873. (setq ido-cr+-nil-def-alternate-behavior-list
  874. (append '(def-nil-command
  875. def-nil-function
  876. def-nil-collection)
  877. ido-cr+-nil-def-alternate-behavior-list)))
  878. (it "should not use empty string default in a command"
  879. (expect
  880. (with-simulated-input "RET"
  881. (call-interactively 'def-nil-command))
  882. :to-equal "blue"))
  883. (it "should not use empty string default in a function"
  884. (expect
  885. (with-simulated-input "RET"
  886. (call-interactively 'cmd-that-calls-def-nil-function))
  887. :to-equal "blue"))
  888. (it "should not use empty string default for a collection"
  889. (expect
  890. (with-simulated-input "RET"
  891. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  892. :to-equal "blue"))))))
  893. ;; (defun ido-cr+-run-all-tests ()
  894. ;; (interactive)
  895. ;; (ert "^ido-cr\\+-"))
  896. ;;; test-ido-completing-read+.el ends here