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

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