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

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