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

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