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

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