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

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