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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  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 "when INHERIT-INPUT-METHOD is non-nil"
  291. (before-each
  292. (spy-on 'ido-completing-read :and-call-through))
  293. (it "should not fall back if `current-input-method' is non-nil"
  294. (expect
  295. (let ((current-input-method nil))
  296. (with-simulated-input "g RET"
  297. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  298. :to-equal "green"))
  299. (expect 'ido-completing-read :to-have-been-called))
  300. (it "should not fall back if `current-input-method' is non-nil"
  301. (expect
  302. (let ((current-input-method 'ucs))
  303. (with-simulated-input "g RET"
  304. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  305. :to-equal "green"))
  306. (expect 'ido-completing-read :not :to-have-been-called)))
  307. (describe "with manual fallback shortcuts"
  308. (it "should not fall back when C-b or C-f is used in the middle of the input"
  309. (expect
  310. ;; C-b/f not at beginning/end of input should not fall back
  311. (with-simulated-input "g C-b C-f RET"
  312. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  313. :to-equal "green"))
  314. (it "should fall back on C-f at end of input"
  315. (expect
  316. ;; C-f at end of input should fall back
  317. (with-simulated-input "g C-f RET"
  318. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  319. :to-equal "g"))
  320. (it "should not fall back from repeated C-b that hits the start of input"
  321. (expect
  322. ;; Repeated C-b should not fall back
  323. (with-simulated-input "g C-b C-b C-b C-b RET"
  324. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  325. :to-equal "green"))
  326. (it "should fall back on C-b at beginning of input (if previous action was not C-b)"
  327. (expect
  328. ;; C-b at beginning of line should fall back (if previous action
  329. ;; was not also C-b)
  330. (with-simulated-input "g C-b x DEL C-b RET"
  331. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  332. :to-equal "g")))
  333. (describe "with a workaround for an bug with non-nil `ido-enable-dot-prefix'"
  334. ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997
  335. ;; for more information on this bug.
  336. (before-each
  337. (setq ido-enable-dot-prefix t))
  338. (it "should not throw an error when \"\" is in the collection"
  339. (expect
  340. (with-simulated-input "RET"
  341. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  342. :to-equal "")
  343. (expect
  344. (with-simulated-input "a a b RET"
  345. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  346. :to-equal "aab")))
  347. (describe "with dynamic collections"
  348. (before-all
  349. (setq my-dynamic-collection
  350. (completion-table-dynamic
  351. (lambda (text)
  352. (cond
  353. ;; Sub-completions for "hello"
  354. ((s-prefix-p "hello" text)
  355. '("hello" "hello-world" "hello-everyone" "hello-universe"))
  356. ;; Sub-completions for "goodbye"
  357. ((s-prefix-p "goodbye" text)
  358. '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
  359. ;; General completions
  360. (t
  361. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill")))))))
  362. (after-all
  363. (setq my-dynamic-collection nil))
  364. (before-each
  365. (setq ido-enable-flex-matching t
  366. ido-confirm-unique-completion nil)
  367. (spy-on 'ido-cr+-update-dynamic-collection
  368. :and-call-through))
  369. (it "should allow selection of dynamically-added completions"
  370. (expect
  371. (with-simulated-input "hello- RET"
  372. (ido-completing-read+ "Say something: " my-dynamic-collection))
  373. :to-equal "hello-world")
  374. (expect 'ido-cr+-update-dynamic-collection
  375. :to-have-been-called))
  376. (it "should allow ido flex-matching of dynamically-added completions"
  377. (expect
  378. (with-simulated-input "hello-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 pressing TAB"
  385. (expect
  386. (with-simulated-input "h TAB -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 do a dynamic update when idle"
  393. (expect
  394. (with-simulated-input
  395. '("h"
  396. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  397. "-ld RET")
  398. (ido-completing-read+ "Say something: " my-dynamic-collection))
  399. :to-equal
  400. "hello-world")
  401. (expect 'ido-cr+-update-dynamic-collection
  402. :to-have-been-called))
  403. (it "should do a dynamic update when there is only one match remaining"
  404. (expect
  405. (with-simulated-input "hell-ld RET"
  406. (ido-completing-read+ "Say something: " my-dynamic-collection))
  407. :to-equal
  408. "hello-world")
  409. (expect 'ido-cr+-update-dynamic-collection
  410. :to-have-been-called))
  411. (it "should not exit with a unique match if new matches are dynamically added"
  412. (expect
  413. (with-simulated-input '("hell TAB -ld RET")
  414. (ido-completing-read+ "Say something: " my-dynamic-collection))
  415. :to-equal
  416. "hello-world")
  417. (expect 'ido-cr+-update-dynamic-collection
  418. :to-have-been-called))
  419. (it "should exit with a match that is still unique after dynamic updating"
  420. (expect
  421. (with-simulated-input '("helic TAB")
  422. (ido-completing-read+ "Say something: " my-dynamic-collection))
  423. :to-equal
  424. "helicopter")
  425. (expect 'ido-cr+-update-dynamic-collection
  426. :to-have-been-called))
  427. (it "should suppress errors raised by dynamic completion updates"
  428. (let ((collection
  429. (completion-table-dynamic
  430. (lambda (text)
  431. (cond
  432. ((equal text "")
  433. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill"))
  434. (t (error "This collection throws an error on a nonempty prefix")))))))
  435. (expect
  436. (with-simulated-input '("hell TAB RET")
  437. (ido-completing-read+ "Say something: " collection))
  438. :to-equal
  439. "hello")))
  440. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  441. (let ((collection
  442. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  443. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  444. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  445. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  446. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  447. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  448. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  449. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  450. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  451. ;; Test the internal function
  452. (expect
  453. (ido-cr+-apply-restrictions
  454. collection
  455. (list (cons nil "bbb")
  456. (cons nil "eee")))
  457. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  458. ;; First verify it without a dynamic collection
  459. (expect
  460. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  461. (ido-completing-read+
  462. "Pick: " collection nil t nil nil (car collection)))
  463. :to-equal "bbb-eee-ggg")
  464. ;; Now test the same with a dynamic collection
  465. (expect
  466. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  467. (ido-completing-read+
  468. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  469. :to-equal "bbb-eee-ggg")))
  470. (describe "with flx-ido-mode"
  471. (before-each
  472. (flx-ido-mode 1)
  473. (flx-ido-reset))
  474. (it "should allow selection of dynamically-added completions"
  475. (expect
  476. (with-simulated-input "hello-w RET"
  477. (ido-completing-read+ "Say something: " my-dynamic-collection))
  478. :to-equal "hello-world")
  479. (expect 'ido-cr+-update-dynamic-collection
  480. :to-have-been-called))
  481. (it "should allow ido flex-matching of dynamically-added completions"
  482. (expect
  483. (with-simulated-input "hello-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 pressing TAB"
  490. (expect
  491. (with-simulated-input "h TAB -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 do a dynamic update when idle"
  498. (expect
  499. (with-simulated-input
  500. '("h"
  501. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  502. "-ld RET")
  503. (ido-completing-read+ "Say something: " my-dynamic-collection))
  504. :to-equal
  505. "hello-world")
  506. (expect 'ido-cr+-update-dynamic-collection
  507. :to-have-been-called))
  508. (it "should do a dynamic update when there is only one match remaining"
  509. (expect
  510. (with-simulated-input "hell-ld RET"
  511. (ido-completing-read+ "Say something: " my-dynamic-collection))
  512. :to-equal
  513. "hello-world")
  514. (expect 'ido-cr+-update-dynamic-collection
  515. :to-have-been-called))
  516. (it "should not exit with a unique match if new matches are dynamically added"
  517. (expect
  518. (with-simulated-input '("hell TAB -ld RET")
  519. (ido-completing-read+ "Say something: " my-dynamic-collection))
  520. :to-equal
  521. "hello-world")
  522. (expect 'ido-cr+-update-dynamic-collection
  523. :to-have-been-called))
  524. (it "should exit with a match that is still unique after dynamic updating"
  525. (expect
  526. (with-simulated-input '("helic TAB")
  527. (ido-completing-read+ "Say something: " my-dynamic-collection))
  528. :to-equal
  529. "helicopter")
  530. (expect 'ido-cr+-update-dynamic-collection
  531. :to-have-been-called))
  532. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  533. (let ((collection
  534. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  535. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  536. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  537. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  538. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  539. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  540. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  541. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  542. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  543. ;; Test the internal function
  544. (expect
  545. (ido-cr+-apply-restrictions
  546. collection
  547. (list (cons nil "bbb")
  548. (cons nil "eee")))
  549. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  550. ;; First verify it without a dynamic collection
  551. (expect
  552. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  553. (ido-completing-read+
  554. "Pick: " collection nil t nil nil (car collection)))
  555. :to-equal "bbb-eee-ggg")
  556. ;; Now test the same with a dynamic collection
  557. (expect
  558. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  559. (ido-completing-read+
  560. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  561. :to-equal "bbb-eee-ggg")))))
  562. (describe "with unusual inputs"
  563. (it "should accept a COLLECTION of symbols"
  564. (expect
  565. (with-simulated-input "g RET"
  566. (ido-completing-read+ "Prompt: " '(blue yellow green)))
  567. :to-equal "green"))
  568. (it "should accept a mix of strings and symbols in COLLECTION"
  569. (expect
  570. (with-simulated-input "g RET"
  571. (ido-completing-read+ "Prompt: " '(blue "yellow" green)))
  572. :to-equal "green"))
  573. (it "should accept symbols in DEF"
  574. (expect
  575. (with-simulated-input "RET"
  576. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '(brown "green")))
  577. :to-equal "brown"))
  578. (it "should accept an alist COLLECTION"
  579. (expect
  580. (with-simulated-input "RET"
  581. (ido-completing-read+
  582. "Prompt: "
  583. '(("blue" . blue-value)
  584. ("yellow" . yellow-value)
  585. (green . green-value))
  586. nil nil nil nil "green"))
  587. :to-equal "green"))
  588. (it "should accept a hash table COLLECTION"
  589. (expect
  590. (with-simulated-input "RET"
  591. (let ((collection (make-hash-table)))
  592. (puthash "blue" 'blue-value collection)
  593. (puthash "yellow" 'yellow-value collection)
  594. (puthash 'green 'green-value collection)
  595. (ido-completing-read+ "Prompt: " collection nil nil nil nil "green")))
  596. :to-equal "green"))
  597. (it "should accept an obarray COLLECTION"
  598. (expect
  599. (with-simulated-input "forward-char RET"
  600. (ido-completing-read+ "Prompt: " obarray #'commandp
  601. t nil nil "backward-char"))
  602. :to-equal "forward-char"))))
  603. (describe "ido-ubiquitous-mode"
  604. ;; Set up a test command that calls `completing-read'
  605. (before-all
  606. (setf (symbol-function 'test-command)
  607. (lambda ()
  608. (interactive)
  609. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  610. ;; Delete the test command
  611. (after-all
  612. (setf (symbol-function 'test-command) nil))
  613. ;; Verify that the mode can be activated
  614. (it "should enable itself properly"
  615. (expect
  616. (progn
  617. (ido-ubiquitous-mode 1)
  618. (with-simulated-input "g RET"
  619. (command-execute 'test-command)))
  620. :to-equal "green"))
  621. (it "should disable itself properly"
  622. (expect
  623. (progn
  624. (ido-ubiquitous-mode 0)
  625. (with-simulated-input "g RET"
  626. (command-execute 'test-command)))
  627. :to-equal "g"))
  628. (describe "with `ido-cr+-function-blacklist'"
  629. (before-all
  630. (setf (symbol-function 'blacklisted-command)
  631. (lambda (arg)
  632. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  633. arg)
  634. (symbol-function 'blacklisted-function)
  635. (lambda ()
  636. (completing-read "Prompt: " '("blue" "yellow" "green")))
  637. (symbol-function 'cmd-that-calls-blacklisted-function)
  638. (lambda ()
  639. (interactive)
  640. (funcall 'blacklisted-function))
  641. (symbol-function 'blacklisted-collection)
  642. (collection-as-function '("blue" "yellow" "green"))))
  643. (after-all
  644. (setf (symbol-function 'blacklisted-command) nil
  645. (symbol-function 'blacklisted-function) nil
  646. (symbol-function 'cmd-that-calls-blacklisted-function) nil
  647. (symbol-function 'blacklisted-collection) nil))
  648. ;; First verify that they work normally before blacklisting them
  649. (describe "when the blacklist is empty"
  650. (it "should not affect a non-blacklisted command"
  651. (expect
  652. (with-simulated-input "g RET"
  653. (call-interactively 'blacklisted-command))
  654. :to-equal "green"))
  655. (it "should not affect a non-blacklisted function"
  656. (expect
  657. (with-simulated-input "g RET"
  658. (call-interactively 'cmd-that-calls-blacklisted-function))
  659. :to-equal "green"))
  660. (it "should not affect a non-blacklisted collection"
  661. (expect
  662. (with-simulated-input "g RET"
  663. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  664. :to-equal "green")))
  665. (describe "when the specified functions are blacklisted"
  666. (before-each
  667. (setq ido-cr+-function-blacklist
  668. (append '(blacklisted-command
  669. blacklisted-function
  670. blacklisted-collection)
  671. ido-cr+-function-blacklist)))
  672. (it "should prevent ido in a blacklisted command"
  673. (expect
  674. (with-simulated-input "g RET"
  675. (call-interactively 'blacklisted-command))
  676. :to-equal "g"))
  677. (it "should prevent ido in a blacklisted function"
  678. (expect
  679. (with-simulated-input "g RET"
  680. (call-interactively 'cmd-that-calls-blacklisted-function))
  681. :to-equal "g"))
  682. (it "should prevent ido with a blacklisted collection"
  683. (expect
  684. (with-simulated-input "g RET"
  685. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  686. :to-equal "g"))))
  687. (describe "with `ido-cr+-function-whitelist'"
  688. (before-all
  689. (setf (symbol-function 'whitelisted-command)
  690. (lambda (arg)
  691. (interactive
  692. (list
  693. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  694. arg)
  695. (symbol-function 'whitelisted-function)
  696. (lambda ()
  697. (completing-read "Prompt: " '("blue" "yellow" "green")))
  698. (symbol-function 'cmd-that-calls-whitelisted-function)
  699. (lambda ()
  700. (interactive)
  701. (funcall 'whitelisted-function))
  702. (symbol-function 'whitelisted-collection)
  703. (lambda (string pred action)
  704. (complete-with-action action '("blue" "yellow" "green") string pred))))
  705. (after-all
  706. (setf (symbol-function 'whitelisted-command) nil
  707. (symbol-function 'whitelisted-function) nil
  708. (symbol-function 'cmd-that-calls-whitelisted-function) nil
  709. (symbol-function 'whitelisted-collection) nil))
  710. (describe "when the whitelist is inactive (i.e. everything is whitelisted)"
  711. (before-each
  712. (setq ido-cr+-function-whitelist nil))
  713. (it "should enable ido in a command"
  714. (expect
  715. (with-simulated-input "g RET"
  716. (call-interactively 'whitelisted-command))
  717. :to-equal "green"))
  718. (it "should enable ido in a function"
  719. (expect
  720. (with-simulated-input "g RET"
  721. (call-interactively 'cmd-that-calls-whitelisted-function))
  722. :to-equal "green"))
  723. (it "should enable ido for a collection"
  724. (expect
  725. (with-simulated-input "g RET"
  726. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  727. :to-equal "green")))
  728. (describe "when the specified functions are whitelisted"
  729. (before-each
  730. (setq ido-cr+-function-whitelist
  731. (append '(whitelisted-command
  732. whitelisted-function
  733. whitelisted-collection)
  734. ido-cr+-function-whitelist)))
  735. (it "should enable ido in a whitelisted command"
  736. (expect
  737. (with-simulated-input "g RET"
  738. (call-interactively 'whitelisted-command))
  739. :to-equal "green"))
  740. (it "should enable ido in a whitelisted function"
  741. (expect
  742. (with-simulated-input "g RET"
  743. (call-interactively 'cmd-that-calls-whitelisted-function))
  744. :to-equal "green"))
  745. (it "should enable ido for a whitelisted collection"
  746. (expect
  747. (with-simulated-input "g RET"
  748. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  749. :to-equal "green")))
  750. (describe "when the whitelist is active but empty (i.e. nothing whitelisted)"
  751. (before-each
  752. (setq ido-cr+-function-whitelist (list nil)))
  753. (it "should prevent ido in a command"
  754. (expect
  755. (with-simulated-input "g RET"
  756. (call-interactively 'whitelisted-command))
  757. :to-equal "g"))
  758. (it "should prevent ido in a function"
  759. (expect
  760. (with-simulated-input "g RET"
  761. (call-interactively 'cmd-that-calls-whitelisted-function))
  762. :to-equal "g"))
  763. (it "should prevent ido for a collection"
  764. (expect
  765. (with-simulated-input "g RET"
  766. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  767. :to-equal "g"))))
  768. (describe "with `ido-cr+-nil-def-alternate-behavior-list'"
  769. (before-all
  770. (setf (symbol-function 'def-nil-command)
  771. (lambda (arg)
  772. (interactive
  773. (list
  774. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))
  775. arg)
  776. (symbol-function 'def-nil-function)
  777. (lambda ()
  778. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  779. (symbol-function 'cmd-that-calls-def-nil-function)
  780. (lambda ()
  781. (interactive)
  782. (funcall 'def-nil-function))
  783. (symbol-function 'def-nil-collection)
  784. (lambda (string pred action)
  785. (complete-with-action action '("blue" "yellow" "green") string pred))))
  786. (after-all
  787. (setf (symbol-function 'def-nil-command) nil
  788. (symbol-function 'def-nil-function) nil
  789. (symbol-function 'cmd-that-calls-def-nil-function) nil
  790. (symbol-function 'def-nil-collection) nil))
  791. (describe "when the specified functions are not in the list"
  792. (before-each
  793. (setq ido-cr+-nil-def-alternate-behavior-list nil))
  794. (it "should use empty string default in a command"
  795. (expect
  796. (with-simulated-input "RET"
  797. (call-interactively 'def-nil-command))
  798. :to-equal ""))
  799. (it "should use empty string default in a function"
  800. (expect
  801. (with-simulated-input "RET"
  802. (call-interactively 'cmd-that-calls-def-nil-function))
  803. :to-equal ""))
  804. (it "should use empty string default for a collection"
  805. (expect
  806. (with-simulated-input "RET"
  807. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  808. :to-equal "")))
  809. (describe "when the specified functions are in the list"
  810. (before-each
  811. (setq ido-cr+-nil-def-alternate-behavior-list
  812. (append '(def-nil-command
  813. def-nil-function
  814. def-nil-collection)
  815. ido-cr+-nil-def-alternate-behavior-list)))
  816. (it "should not use empty string default in a command"
  817. (expect
  818. (with-simulated-input "RET"
  819. (call-interactively 'def-nil-command))
  820. :to-equal "blue"))
  821. (it "should not use empty string default in a function"
  822. (expect
  823. (with-simulated-input "RET"
  824. (call-interactively 'cmd-that-calls-def-nil-function))
  825. :to-equal "blue"))
  826. (it "should not use empty string default for a collection"
  827. (expect
  828. (with-simulated-input "RET"
  829. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  830. :to-equal "blue"))))))
  831. ;; (defun ido-cr+-run-all-tests ()
  832. ;; (interactive)
  833. ;; (ert "^ido-cr\\+-"))
  834. ;;; test-ido-completing-read+.el ends here